我遇到了一个奇怪的情况,一个状态变量在一个组件中成功更新,但在另一个组件中没有更新。我成功地改变了state
,因为我可以看到它反映在我触发dispatch
的组件中。但在另一个组件,包裹在相同的Provider
,我看到没有变化。
其他Stack Overflow答案似乎大多建议不要直接改变状态,但在这里我没有这样做-我正在调度一个动作来更新状态,类似于您在Redux语法中看到的。
CodeSandbox演示TeaSettingsContext.tsx
const TeaSettingsContext = createContext<{ state: State; dispatch: Dispatch } | undefined>(undefined);
const teaSettingsReducer = (state: State, action: Action) => {
switch (action.type) {
case TeaSettingsActions.ChooseTea: {
return { ...state, chosenTea: action.payload };
}
case TeaSettingsActions.ChangeStrength: {
return { ...state, desiredStrength: action.payload };
}
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
}
};
export function TeaSettingsProvider({
children,
}: TeaSettingsProviderProps): Context {
const [state, dispatch] = useReducer(teaSettingsReducer, {
chosenTea: {},
desiredStrength: 0.5,
});
return (
<TeaSettingsContext.Provider value={{ state, dispatch }}>
{children}
</TeaSettingsContext.Provider>
);
}
export const useTeaSettingsContext = (): TeaSettingsContext => {
const context = useContext(TeaSettingsContext);
if (context === undefined) {
return;
}
return context;
};
这里是"成功"组件:
Home.tsx
<TeaSettingsProvider>
<StrengthSlider />
</TeaSettingsProvider>
StrengthSlider.tsx
export default function StrengthSlider(): ReactNode {
const { state, dispatch } = useTeaSettingsContext();
console.log(state.desiredStrength) // logs the increment on each press.
const incrementStrength = () => {
dispatch({
payload: state.desiredStrength + 0.1,
type: "change-strength",
});
};
return (
<Box position="relative" w="100%">
<Button title="Press" onPress={incrementStrength} />
<Text>{state.desiredStrength}</Text>
</Box>
);
}
…不成功的重新渲染发生在这个组件中:
TeaPage.tsx
const Component = () => {
if (type === "tea") {
return data.map((teaObj) => (
<TeaCard id={teaObj.id} teaData={teaObj.data} key={teaObj.id} />
));
}
};
return (
<TeaSettingsProvider>
<Component />
</TeaSettingsProvider>
);
TeaCard.tsx
export function TeaCard({ id, teaData }: TeaCardProps): ReactNode {
const { state, dispatch } = useTeaSettingsContext();
console.log(state.desiredStrength); // this always logs the starting value of 0.5 and doesn't log each time I press the button above.
// ...
}
仅供参考:基于我的代码Kent C Dodds的文章:https://kentcdodds.com/blog/how-to-use-react-context-effectively
我想你误解了上下文是如何工作的。
基本上(如react文档建议):
每个Context对象都带有一个Provider React组件,它允许使用组件来订阅上下文更改。
Provider组件接受传递给消费的值prop作为此提供程序的后代的组件。一个提供者可以是连接到许多消费者。提供程序可以嵌套以覆盖
所有作为提供者后代的消费者都将重新呈现当Provider的值改变时。从提供程序到其后代消费者(包括.contextType和useContext)不受shouldComponentUpdate方法的约束,所以即使祖先组件跳过更新,消费者也会被更新。
简而言之,当您创建具有值的提供者时,您可以从多个消费者(需要是该提供者的子用户)访问该信息。到目前为止一切顺利。
除了:Home.tsx
和TeaPage.tsx
都初始化上下文的不同实例(因此每个实例都有自己的提供程序)。为了解决这个问题,你应该只在Home
和TeaPage
的父组件中创建一个提供商。
React上下文不像redux那样工作(在redux中,你有一个可以从任何地方访问的全局存储)。相反,它为您提供了为大型组件(在子组件中使用)创建上下文(包含可重用数据)的选项。
还请注意开头引用的最后一段:基本上,当上下文改变时,这会导致所有消费者重新渲染(这有时可能导致一些性能问题),所以明智地使用它。这里有一篇关于这个的短文。