useReducer 状态显然不是反应性的
useReducer state is apparently not reactive
我有一个想法来构建一个有点像 Vuex 的 React 商店。我想要一个包装器组件接收一个 object 的值,并使用 useContext
提供一个 object 返回,将 state
和 set
附加到每个键object.
例如,如果您将应用包装在 <StoreProvider value={{ color: 'red' }}>
之类的内容中,您可以在任何 child 中写入 const { color } = useContext(StoreContext)
,将其值与 color.state
一起使用,然后调用color.set('blue')
全局更改该值。
我在商店写了 best-attempt useReducer
:
import React, {
createContext,
useContext,
useReducer
} from 'react'
const StoreContext = createContext(null)
export const useStoreContext = () => useContext(StoreContext)
export const StoreProvider = ({ children, value }) => {
const reducer = (state, action) => {
state[action.type].state = action.payload
return state
}
Object.keys(value).map(key => {
let state = value[key]
value[key] = {
state: state,
set: (setter) => dispatch({ type: key, payload: setter })
}
})
const [store, dispatch] = useReducer(reducer, value)
return (
<StoreContext.Provider value={store}>
{children}
</StoreContext.Provider>
)
}
还有一个小演示,看看它是否有效:
import React from 'react'
import ReactDOM from 'react-dom'
import { StoreProvider, useStoreContext } from './store'
const App = () => {
const { color } = useStoreContext()
return (
<>
<h1>{color.state}</h1>
<button onClick={() => color.set('yellow')}>Change color</button>
<button onClick={() => console.log(color)}>Log</button>
</>
)
}
ReactDOM.render(
<StoreProvider value={{
color: 'orange'
}}>
<App />
</StoreProvider>,
document.getElementById('root')
)
在上面,<h1>
正确呈现为 'orange',但当 set('yellow')
为 运行 时不会更新为 'yellow'。但是,如果我记录颜色,state
已更新为 'yellow'——我猜这意味着状态不是反应性的。
我是不是真的做了什么蠢事?我对 React 很陌生,之前从未使用过 useReducer
。
我认为在这种情况下,只要要管理的状态不是那么复杂,您就可以使用 useState 挂钩来简化您的逻辑:
export const StoreProvider = ({ children, value }) => {
const { color } = value;
const [currentColor, setCurrentColor] = useState(color);
return (
<StoreContext.Provider value={{ currentColor, setCurrentColor }}>
{children}
</StoreContext.Provider>
);
};
这里是 App 组件:
const App = () => {
const { currentColor, setCurrentColor } = useStoreContext();
return (
<>
<h1>{currentColor}</h1>
<button onClick={() => setCurrentColor('yellow')}>Change color</button>
<button onClick={() => console.log(currentColor)}>Log</button>
</>
);
};
我觉得你错过了 React 中非常重要的一点,你永远不应该改变状态对象。 state[action.type].state = action.payload
是状态突变。在突变之上,您只需 return 相同的状态对象。为了使 React 状态更新正常工作,您必须 return new object references.
const reducer = (state, action) => {
return {
...state, // <-- shallow copy state
[action.type]: {
...state[action.type], // <-- shallow copy nested state
state: action.payload,
}
};
}
您还滥用了 array.map
回调;你真的应该使用 forEach
是你正在迭代一个数组并发出副作用。 array.map
被认为是纯函数。
Object.keys(value).forEach(key => {
let state = value[key]
value[key] = {
state: state,
set: (setter) => dispatch({ type: key, payload: setter })
}
});
我无法使您的初始 value
映射正常工作,似乎它正在做一些额外的属性嵌套,这些属性与您在 UI 中访问或更新的方式不完全匹配.我建议使用以下方法来计算您的初始减速器状态值。迭代传递给上下文的 value
对象的键值对数组,减少为具有相同键和值的对象,并且 setter 正确嵌套。
const StoreProvider = ({ children, value }) => {
const reducer = (state, action) => {
return {
...state,
[action.type]: {
...state[action.type],
state: action.payload
}
};
};
const state = Object.entries(value).reduce(
(state, [key, value]) => ({
...state,
[key]: {
state: value,
set: (setter) => dispatch({ type: key, payload: setter })
}
}),
{}
);
const [store, dispatch] = useReducer(reducer, state);
return (
<StoreContext.Provider value={store}>{children}</StoreContext.Provider>
);
};
我有一个想法来构建一个有点像 Vuex 的 React 商店。我想要一个包装器组件接收一个 object 的值,并使用 useContext
提供一个 object 返回,将 state
和 set
附加到每个键object.
例如,如果您将应用包装在 <StoreProvider value={{ color: 'red' }}>
之类的内容中,您可以在任何 child 中写入 const { color } = useContext(StoreContext)
,将其值与 color.state
一起使用,然后调用color.set('blue')
全局更改该值。
我在商店写了 best-attempt useReducer
:
import React, {
createContext,
useContext,
useReducer
} from 'react'
const StoreContext = createContext(null)
export const useStoreContext = () => useContext(StoreContext)
export const StoreProvider = ({ children, value }) => {
const reducer = (state, action) => {
state[action.type].state = action.payload
return state
}
Object.keys(value).map(key => {
let state = value[key]
value[key] = {
state: state,
set: (setter) => dispatch({ type: key, payload: setter })
}
})
const [store, dispatch] = useReducer(reducer, value)
return (
<StoreContext.Provider value={store}>
{children}
</StoreContext.Provider>
)
}
还有一个小演示,看看它是否有效:
import React from 'react'
import ReactDOM from 'react-dom'
import { StoreProvider, useStoreContext } from './store'
const App = () => {
const { color } = useStoreContext()
return (
<>
<h1>{color.state}</h1>
<button onClick={() => color.set('yellow')}>Change color</button>
<button onClick={() => console.log(color)}>Log</button>
</>
)
}
ReactDOM.render(
<StoreProvider value={{
color: 'orange'
}}>
<App />
</StoreProvider>,
document.getElementById('root')
)
在上面,<h1>
正确呈现为 'orange',但当 set('yellow')
为 运行 时不会更新为 'yellow'。但是,如果我记录颜色,state
已更新为 'yellow'——我猜这意味着状态不是反应性的。
我是不是真的做了什么蠢事?我对 React 很陌生,之前从未使用过 useReducer
。
我认为在这种情况下,只要要管理的状态不是那么复杂,您就可以使用 useState 挂钩来简化您的逻辑:
export const StoreProvider = ({ children, value }) => {
const { color } = value;
const [currentColor, setCurrentColor] = useState(color);
return (
<StoreContext.Provider value={{ currentColor, setCurrentColor }}>
{children}
</StoreContext.Provider>
);
};
这里是 App 组件:
const App = () => {
const { currentColor, setCurrentColor } = useStoreContext();
return (
<>
<h1>{currentColor}</h1>
<button onClick={() => setCurrentColor('yellow')}>Change color</button>
<button onClick={() => console.log(currentColor)}>Log</button>
</>
);
};
我觉得你错过了 React 中非常重要的一点,你永远不应该改变状态对象。 state[action.type].state = action.payload
是状态突变。在突变之上,您只需 return 相同的状态对象。为了使 React 状态更新正常工作,您必须 return new object references.
const reducer = (state, action) => {
return {
...state, // <-- shallow copy state
[action.type]: {
...state[action.type], // <-- shallow copy nested state
state: action.payload,
}
};
}
您还滥用了 array.map
回调;你真的应该使用 forEach
是你正在迭代一个数组并发出副作用。 array.map
被认为是纯函数。
Object.keys(value).forEach(key => {
let state = value[key]
value[key] = {
state: state,
set: (setter) => dispatch({ type: key, payload: setter })
}
});
我无法使您的初始 value
映射正常工作,似乎它正在做一些额外的属性嵌套,这些属性与您在 UI 中访问或更新的方式不完全匹配.我建议使用以下方法来计算您的初始减速器状态值。迭代传递给上下文的 value
对象的键值对数组,减少为具有相同键和值的对象,并且 setter 正确嵌套。
const StoreProvider = ({ children, value }) => {
const reducer = (state, action) => {
return {
...state,
[action.type]: {
...state[action.type],
state: action.payload
}
};
};
const state = Object.entries(value).reduce(
(state, [key, value]) => ({
...state,
[key]: {
state: value,
set: (setter) => dispatch({ type: key, payload: setter })
}
}),
{}
);
const [store, dispatch] = useReducer(reducer, state);
return (
<StoreContext.Provider value={store}>{children}</StoreContext.Provider>
);
};