[英]How to implement observable watching for value in React Context
假設我有一個Parent
組件提供一個Context
,它是一個Store
對象。 為簡單起見,假設這個 Store 有一個值和一個函數來更新這個值
class Store {
// value
// function updateValue() {}
}
const Parent = () => {
const [rerender, setRerender] = useState(false);
const ctx = new Store();
return (
<SomeContext.Provider value={ctx}>
<Children1 />
<Children2 />
.... // and alot of component here
</SomeContext.Provider>
);
};
const Children1 = () => {
const ctx = useContext(SomeContext);
return (<div>{ctx.value}</div>)
}
const Children2 = () => {
const ctx = useContext(SomeContext);
const onClickBtn = () => {ctx.updateValue('update')}
return (<button onClick={onClickBtn}>Update Value </button>)
}
所以基本上Children1
會顯示值,而在Children2
組件中,有一個按鈕來更新值。
所以我現在的問題是當Children2
更新 Store 值時,Children1不會重新呈現。 以反映新的價值。
堆棧溢出的一種解決方案是here 。 這個想法是在Parent
創建一個state
並使用它來將context
傳遞給孩子。 這將有助於重新渲染Children1
因為Parent
被重新渲染。 但是,我不希望Parent
重新渲染,因為在Parent
有很多其他組件。 我只希望Children1
重新渲染。
那么有沒有關於如何解決這個問題的解決方案? 我應該使用 RxJS 進行響應式編程還是應該更改代碼中的某些內容? 謝謝
您可以使用像 redux lib 這樣的上下文,如下所示
這很容易使用,以后如果你想轉移到 redux,你只需要更改存儲文件,整個狀態管理內容將轉移到 redux 或任何其他庫。
運行示例: https : //stackblitz.com/edit/reactjs-usecontext-usereducer-state-management
文章: https : //rsharma0011.medium.com/state-management-with-react-hooks-and-context-api-2968a5cf5c83
減速器.js
import { combineReducers } from "./Store";
const countReducer = (state = { count: 0 }, action) => {
switch (action.type) {
case "INCREMENT":
return { ...state, count: state.count + 1 };
case "DECREMENT":
return { ...state, count: state.count - 1 };
default:
return state;
}
};
export default combineReducers({ countReducer });
商店.js
import React, { useReducer, createContext, useContext } from "react";
const initialState = {};
const Context = createContext(initialState);
const Provider = ({ children, reducers, ...rest }) => {
const defaultState = reducers(undefined, initialState);
if (defaultState === undefined) {
throw new Error("reducer's should not return undefined");
}
const [state, dispatch] = useReducer(reducers, defaultState);
return (
<Context.Provider value={{ state, dispatch }}>{children}</Context.Provider>
);
};
const combineReducers = reducers => {
const entries = Object.entries(reducers);
return (state = {}, action) => {
return entries.reduce((_state, [key, reducer]) => {
_state[key] = reducer(state[key], action);
return _state;
}, {});
};
};
const Connect = (mapStateToProps, mapDispatchToProps) => {
return WrappedComponent => {
return props => {
const { state, dispatch } = useContext(Context);
let localState = { ...state };
if (mapStateToProps) {
localState = mapStateToProps(state);
}
if (mapDispatchToProps) {
localState = { ...localState, ...mapDispatchToProps(dispatch, state) };
}
return (
<WrappedComponent
{...props}
{...localState}
state={state}
dispatch={dispatch}
/>
);
};
};
};
export { Context, Provider, Connect, combineReducers };
應用程序.js
import React from "react";
import ContextStateManagement from "./ContextStateManagement";
import CounterUseReducer from "./CounterUseReducer";
import reducers from "./Reducers";
import { Provider } from "./Store";
import "./style.css";
export default function App() {
return (
<Provider reducers={reducers}>
<ContextStateManagement />
</Provider>
);
}
組件.js
import React from "react";
import { Connect } from "./Store";
const ContextStateManagement = props => {
return (
<>
<h3>Global Context: {props.count} </h3>
<button onClick={props.increment}>Global Increment</button>
<br />
<br />
<button onClick={props.decrement}>Global Decrement</button>
</>
);
};
const mapStateToProps = ({ countReducer }) => {
return {
count: countReducer.count
};
};
const mapDispatchToProps = dispatch => {
return {
increment: () => dispatch({ type: "INCREMENT" }),
decrement: () => dispatch({ type: "DECREMENT" })
};
};
export default Connect(mapStateToProps, mapDispatchToProps)(
ContextStateManagement
);
如果你不希望你的Parent
組件在狀態更新時重新渲染,那么你使用了錯誤的狀態管理模式,完全。 相反,您應該使用Redux 之類的東西,它從 React 組件樹中完全刪除“狀態”,並允許組件直接訂閱狀態更新。
Redux 將只允許訂閱特定存儲值的組件僅在這些值更新時更新。 因此,您的父組件和分派更新操作的子組件不會更新,而只有訂閱狀態的子組件會更新。 這是非常有效的!
React 組件僅在以下任一情況下更新
props
變了state
改變state
改變了正如您所指出的, state
需要保存在父組件中並傳遞給上下文。
你的要求是
state
改變時,父級不應重新渲染。Child1
應該在state
改變時重新渲染const SomeContext = React.createContext(null);
孩子 1 和 2
const Child1 = () => {
const ctx = useContext(SomeContext);
console.log(`child1: ${ctx}`);
return <div>{ctx.value}</div>;
};
const Child2 = () => {
const ctx = useContext(UpdateContext);
console.log("child 2");
const onClickBtn = () => {
ctx.updateValue("updates");
};
return <button onClick={onClickBtn}>Update Value </button>;
};
現在添加state
的上下文提供程序
const Provider = (props) => {
const [state, setState] = useState({ value: "Hello" });
const updateValue = (newValue) => {
setState({
value: newValue
});
};
useEffect(() => {
document.addEventListener("stateUpdates", (e) => {
updateValue(e.detail);
});
}, []);
const getState = () => {
return {
value: state.value,
updateValue
};
};
return (
<SomeContext.Provider value={getState()}>
{props.children}.
</SomeContext.Provider>
);
};
呈現兩個父組件Child1
和Child2
const Parent = () => {
// This is only logged once
console.log("render parent");
return (
<Provider>
<Child1 />
<Child2 />
</Provider>
);
};
現在對於第一個要求,當您通過單擊child2
的按鈕更新state
, Parent
將不會重新呈現,因為Context Provider
不是其父級。
當state
改變時只Child1
和Child2
將重新呈現。
現在對於第二個要求僅Child1
需要重新呈現。
為此,我們需要重構一下。
這就是反應性的來源。 只要Child2
是 Provider 的孩子,當state
改變時它也會更新。
從提供程序中取出Child2
。
const Parent = () => {
console.log("render parent");
return (
<>
<Provider>
<Child1 />
</Provider>
<Child2 />
</>
);
};
現在我們需要一些方法來更新Child2
的狀態。
為簡單起見,我在這里使用了瀏覽器自定義事件。 你可以使用 RxJs。
Provider
正在監聽狀態更新,當單擊按鈕並更新狀態時Child2
將觸發事件。
const Provider = (props) => {
const [state, setState] = useState({ value: "Hello" });
const updateValue = (e) => {
setState({
value: e.detail
});
};
useEffect(() => {
document.addEventListener("stateUpdates", updateValue);
return ()=>{
document.addEventListener("stateUpdates", updateValue);
}
}, []);
return (
<SomeContext.Provider value={state}>{props.children}</SomeContext.Provider>
);
};
const Child2 = () => {
console.log("child 2");
const onClickBtn = () => {
const event = new CustomEvent("stateUpdates", { detail: "Updates" });
document.dispatchEvent(event);
};
return <button onClick={onClickBtn}>Update Value </button>;
};
注意:Child2 將無法訪問上下文
我希望這有助於讓我知道如果你不明白什么。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.