Fre*_*dy. 28 javascript reducers reactjs react-redux react-hooks
我不是 Javascript 专家,所以我想知道是否有人有一种“优雅”的方式来组合多个减速器来创建全局状态(如 Redux)。当一个状态更新多个组件等时不影响性能的功能。
假设我有一个 store.js
import React, { createContext, useReducer } from "react";
import Rootreducer from "./Rootreducer"
export const StoreContext = createContext();
const initialState = {
....
};
export const StoreProvider = props => {
const [state, dispatch] = useReducer(Rootreducer, initialState);
return (
<StoreContext.Provider value={[state, dispatch]}>
{props.children}
<StoreContext.Provider>
);
};
Run Code Online (Sandbox Code Playgroud)
Rootreducer.js
import Reducer1 from "./Reducer1"
import Reducer2 from "./Reducer2"
import Reducer3 from "./Reducer3"
import Reducer4 from "./Reducer4"
const rootReducer = combineReducers({
Reducer1,
Reducer2,
Reducer3,
Reducer4
})
export default rootReducer;
Run Code Online (Sandbox Code Playgroud)
for*_*d04 29
combineReducers
)最常见的方法是让每个 reducer 管理自己的状态属性(“切片”):
const combineReducers = (slices) => (state, action) =>
Object.keys(slices).reduce( // use for..in loop, if you prefer it
(acc, prop) => ({
...acc,
[prop]: slices[prop](acc[prop], action),
}),
state
);
Run Code Online (Sandbox Code Playgroud)
例子:
import a from "./Reducer1";
import b from "./Reducer2";
const initialState = { a: {}, b: {} }; // some state for props a, b
const rootReducer = combineReducers({ a, b });
const StoreProvider = ({ children }) => {
const [state, dispatch] = useReducer(rootReducer, initialState);
// Important(!): memoize array value. Else all context consumers update on *every* render
const store = React.useMemo(() => [state, dispatch], [state]);
return (
<StoreContext.Provider value={store}> {children} </StoreContext.Provider>
);
};
Run Code Online (Sandbox Code Playgroud)
在任意形状的状态上依次应用多个 reducer ,类似于reduce-reducers:
const reduceReducers = (...reducers) => (state, action) =>
reducers.reduce((acc, nextReducer) => nextReducer(acc, action), state);
Run Code Online (Sandbox Code Playgroud)
例子:
const rootReducer2 = reduceReducers(a, b);
// rest like in first variant
Run Code Online (Sandbox Code Playgroud)
useReducer
Hook您还可以结合多个useReducer
s 的dispatch 和/或 state ,例如:
const combineDispatch = (...dispatches) => (action) =>
dispatches.forEach((dispatch) => dispatch(action));
Run Code Online (Sandbox Code Playgroud)
例子:
const [s1, d1] = useReducer(a, {}); // some init state {}
const [s2, d2] = useReducer(b, {}); // some init state {}
// don't forget to memoize again
const combinedDispatch = React.useCallback(combineDispatch(d1, d2), [d1, d2]);
const combinedState = React.useMemo(() => ({ s1, s2, }), [s1, s2]);
// This example uses separate dispatch and state contexts for better render performance
<DispatchContext.Provider value={combinedDispatch}>
<StateContext.Provider value={combinedState}> {children} </StateContext.Provider>
</DispatchContext.Provider>;
Run Code Online (Sandbox Code Playgroud)
以上是最常见的变体。还有类似use-combined-reducers
这些案例的库。最后,看看下面结合了combineReducers
和 的示例reduceReducers
:
const combineReducers = (slices) => (state, action) =>
Object.keys(slices).reduce( // use for..in loop, if you prefer it
(acc, prop) => ({
...acc,
[prop]: slices[prop](acc[prop], action),
}),
state
);
Run Code Online (Sandbox Code Playgroud)
import a from "./Reducer1";
import b from "./Reducer2";
const initialState = { a: {}, b: {} }; // some state for props a, b
const rootReducer = combineReducers({ a, b });
const StoreProvider = ({ children }) => {
const [state, dispatch] = useReducer(rootReducer, initialState);
// Important(!): memoize array value. Else all context consumers update on *every* render
const store = React.useMemo(() => [state, dispatch], [state]);
return (
<StoreContext.Provider value={store}> {children} </StoreContext.Provider>
);
};
Run Code Online (Sandbox Code Playgroud)
如果您只是想在没有任何第三方库的情况下实现组合减速器功能,请按以下步骤操作。(REF: Redux source/code) 工作代码在这里https://codepen.io/rajeshpillai/pen/jOPWYzL?editors=0010
我创建了两个减速器,一个 dateReducer 和另一个 counterReducer。我用它作为
const [state, dispatch] = useReducer(combineReducer({ counter: counterReducer, date: dateReducer }), initialState);
combineReducers 代码
function combineReducers(reducers) {
return (state = {}, action) => {
const newState = {};
for (let key in reducers) {
newState[key] = reducers[key](state[key], action);
}
return newState;
}
}
Run Code Online (Sandbox Code Playgroud)
用法:提取各自的状态
const { counter, date } = state;
Run Code Online (Sandbox Code Playgroud)
注意:如果您愿意,您可以添加更多类似 redux 的功能。
完整的工作代码(以防 codepen 关闭 :))
const {useReducer, useEffect} = React;
function dateReducer(state, action) {
switch(action.type) {
case "set_date":
return action.payload;
break;
default:
return state;
}
}
function counterReducer(state, action) {
console.log('cr:', state);
switch (action.type) {
case 'increment': {
return state + 1;
}
case 'decrement': {
return state - 1;
}
default:
return state;
}
}
function combineReducers(reducers) {
return (state = {}, action) => {
const newState = {};
for (let key in reducers) {
newState[key] = reducers[key](state[key], action);
}
return newState;
}
}
const initialState = {
counter: 0,
date: new Date
};
function App() {
const [state, dispatch] = useReducer(combineReducers({
counter: counterReducer,
date: dateReducer
}), initialState);
console.log("state", state);
const { counter, date } = state;
return (
<div className="app">
<h3>Counter Reducer</h3>
<div className="counter">
<button onClick={() =>
dispatch({ type: 'increment'})}>+
</button>
<h2>{counter.toString()}</h2>
<button onClick={() =>
dispatch({ type: 'decrement'})}>-
</button>
</div>
<hr/>
<h3>Date Reducer</h3>
{date.toString()}
<button className="submit"
type="submit"
onClick={() =>
dispatch({ type: 'set_date', payload:new Date })}>
Set Date
</button>
</div>
);
}
const rootElement = document.querySelector("#root");
ReactDOM.render(<App />, rootElement);
Run Code Online (Sandbox Code Playgroud)
注意:这是一个快速技巧(仅用于学习和演示目的)
归档时间: |
|
查看次数: |
23831 次 |
最近记录: |