这里是React-Redux,Redux状态管理的React绑定库,使React组件从Redux store中读取数据,并且向store分发actions以更新数据1
2
3npm install --save react-redux
或
yarn add react-redux
另,有typescript的definition包:@types/react-redux
以Provider组件载入Redux store1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import 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 store1
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
38import 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 最后返回新的state1
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
32function 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
59export 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
47const 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),
};
};