0%

Redux

这里是React-Redux,Redux状态管理的React绑定库,使React组件从Redux store中读取数据,并且向store分发actions以更新数据

1
2
3
npm install --save react-redux

yarn add react-redux

另,有typescript的definition包:@types/react-redux

以Provider组件载入Redux store

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from "react";
import ReactDOM from "react-dom";

import { Provider } from "react-redux";
import store from "./store";

import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);

使用Hooks调用Redux store
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import {
decrement,
increment,
incrementByAmount,
incrementAsync,
selectCount,
} from './counterSlice'
import styles from './Counter.module.css'

export function Counter() {
const count = useSelector(selectCount)
const dispatch = useDispatch()

return (
<div>
<div className={styles.row}>
<button
className={styles.button}
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
+
</button>
<span className={styles.value}>{count}</span>
<button
className={styles.button}
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
-
</button>
</div>
{/* omit additional rendering output here */}
</div>
)
}

store

一个待办列表(todo list)的store示例

1
2
3
4
5
6
7
8
9
10
{
todos: [{
text: 'Eat food',
completed: true
}, {
text: 'Exercise',
completed: false
}],
visibilityFilter: 'SHOW_COMPLETED'
}

store管理状态,状态不可随意改变

action

action用以触发状态更新 形如

1
{type:'action_nameXX', value:'any value'}

reduce

联系state和action的方法 即传入state action 根据action更新state 最后返回新的state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function visibilityFilter(state = 'SHOW_ALL', action) {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter
default:
return state
}
}

function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
text: action.text,
completed: false
}
]
case 'COMPLETE_TODO':
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: true
})
}
return todo
})
default:
return state
}
}

Flux Immutable

数据流

createStore
useSelector
dispatch
middleware

语法糖

createSlice

关于state tree极其reducer的嵌套

从vuex迁移 原vuex代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
export const initialState = () => ({
data: {
nextID: 1,
index: {},
imageIDs: [],
dicomIDs: [],
modelIDs: [],
labelmapIDs: [],
vtkCache: {},
},

// track the mapping from volumeID to data ID
dicomVolumeToDataID: {},
selectedBaseImage: NO_SELECTION,

// data-data associations, in parent-of or child-of relationships.
// is used for cascaded deletes
dataAssoc: {
childrenOf: {},
parentOf: {},
},
});

export default (deps) =>
new Vuex.Store({
modules: {
dicom: dicom(deps),
visualization: visualization(deps),
widgets: widgets(deps),
annotations: annotations(deps),
measurements: measurements(deps),
},

state: initialState(),

getters: {
visibleLabelmaps(state) {
return state.data.labelmapIDs.filter(
(id) => state.dataAssoc.parentOf[id] === state.selectedBaseImage
);
},
sceneObjectIDs(state, getters) {
const { selectedBaseImage, data } = state;
const order = [].concat(getters.visibleLabelmaps, data.modelIDs);
if (selectedBaseImage !== NO_SELECTION) {
order.unshift(selectedBaseImage);
}
return order;
},
},

mutations: {
...datasets.mutations,
},

actions: {
...datasets.makeActions(deps),
},
});

redux:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
const preloadedState = {
data: {
nextID: 1,
index: {},
imageIDs: [],
dicomIDs: [],
modelIDs: [],
labelmapIDs: [],
vtkCache: {},
},

// track the mapping from volumeID to data ID
dicomVolumeToDataID: {},
selectedBaseImage: -1,

// data-data associations, in parent-of or child-of relationships.
// is used for cascaded deletes
dataAssoc: {
childrenOf: {},
parentOf: {},
},
widgets: { focusedWidget: -1, widgetList: [], activeWidgetID: -1 },
annotations: {
selectedLabelmap: -1,
currentLabelFor: {}, // labelmap ID -> currently selected label
labels: {}, // labelmapID -> label -> color
radius: 0,
radiusRange: [1, 100],
},
measurements: {
widgets: [], // list of widget ids
measurements: {}, // widget ID -> opaque measurement obj
// widget/measurement parent is the base image association
parents: {}, // data ID -> [widget ID]
widgetParent: {}, // widget ID -> data ID
},
};
const store = createStore(reducer, preloadedState);

const reducer = (state, action) => {
return {
...state,
widgets: widgets(state.widgets, action),
annotations: annotations(state.annotations, action),
measurements: widgets(state.measurements, action),
};
};