[英]What counts as mutating state in React?
背景在頂部,我的實際問題在底部很簡單,但我提供了上下文,以防我完全錯誤地解決這個問題,而我的問題甚至不相關。 我只使用了大約兩周的反應。
我要做的是創建一個 singleton,可重復使用的背景,可以通過單擊它或單擊使用背景的元素上的控件來關閉它。 這是為了避免在 DOM 中的多個位置渲染多個背景(例如,將背景與每種不同類型的模態、側邊抽屜或內容預覽進行分組)或為背景的 state 提供多個真實來源。
我所做的是創建背景本身,它不會被導出
const Backdrop = props => (
props.show ? <div onClick={props.onClose} className={classes.Backdrop}></div> : null
);
我還創建了一個背景上下文,由 WithBackdrop 高階 class 組件管理,該組件管理背景的 state 並相應地更新上下文
class WithBackdrop extends Component {
state = {
show: true,
closeListeners: []
}
show() {
this.setState({ show: true });
}
hide() {
this.state.closeListeners.map(f => f());
this.setState({ show: false, closeListeners: [] });
}
registerCloseListener(cb) {
// this.setState({ closeListeners: [...this.state.closeListeners, cb]});
// Does this count as mutating state?
this.state.closeListeners.push(cb);
}
render() {
const contextData = {
isShown: this.state.show,
show: this.show.bind(this),
hide: this.hide.bind(this),
registerCloseListener: this.registerCloseListener.bind(this)
};
return (
<BackdropContext.Provider value={contextData}>
<Backdrop show={this.state.show} onClose={this.hide.bind(this)} />
{this.props.children}
</BackdropContext.Provider>
);
}
}
export default WithBackdrop;
我還導出了一個'backdropable' HOC,它用上下文消費者包裝了一個組件
export const backdropable = Component => (props) => (
<BackdropContext.Consumer>
{value => <Component {...props} backdropContext={value}/>}
</BackdropContext.Consumer>
);
此 API 的用法如下: 包裝您希望可能具有背景的布局/應用程序部分,並為將激活背景的任何組件提供上下文。 “Backdropable”只是我用來表示“可以觸發背景”的一個懶字(此處未顯示,但我使用的是 TypeScript,作為接口名稱更有意義)。 可背景組件可以調用 show() 或 hide(),而不必擔心可能觸發背景的其他組件,或者背景 state 的多個事實來源。
然而,我遇到的最后一個問題是如何觸發可隱藏組件關閉處理程序? 我決定 WithBackdrop HOC 將維護一個偵聽器列表,以便需要在背景關閉時通過單擊背景(而不是通過該可背景組件的關閉按鈕或其他東西)做出反應的組件。 這是我用來測試的模態組件
const modal = (props) => {
props.backdropContext.registerCloseListener(props.onClose);
return (
<div
className={[
classes.Modal,
(props.show ? '' : classes.hidden)
].join(' ')}>
{props.children}
<button onClick={() => {
props.onClose();
props.backdropContext.hide()
}}>Cancel</button>
<button onClick={props.onContinue}>Continue</button>
</div>
)
}
export default backdropable(modal);
據我了解,最好的做法是永遠不要改變 state。 我的問題是,推送到 state 中維護的數組是否算作變異 state,我應該從中得到什么潛在的不良后果? 我應該每次都將數組復制到帶有新元素的新數組中,還是如果我嘗試更改 state 成員的引用,我只會得到未定義的 React 行為。 據我了解,react 只是對前一個和下一個 state 進行淺顯的比較以確定重新渲染並為更復雜的比較提供實用程序,所以這應該沒問題吧? 原因是數組復制方法觸發了重新渲染,然后模態嘗試重新注冊 closeListener,然后 WithBackdrop 嘗試再次添加它......我得到一個無限的 state 更新循環。
即使簡單地推送到同一個數組沒有任何問題,您認為 go 是否有更好的方法來執行此操作?
謝謝,我真誠地感謝任何試圖回答這個長問題的人。
編輯: this.setState({ closeListeners: [...this.state.closeListeners, cb]});
導致無限狀態更新循環。
在 React 中改變 state 是當您更改任何值或在 state 中引用 object 而不使用setState
時。
據我了解,最好的做法是永遠不要改變 state。 我的問題是,推送到 state 中維護的數組是否算作變異 state,
是的
我應該期待什么潛在的不良后果?
您可以期望更改 state 的值而看不到 ui 更新。
我是否應該每次都將數組復制到一個帶有新元素的新數組中,
是的:
const things = [...this.state.things]
// change things
this.setState({ things })
或者如果我嘗試更改 state 成員的引用,我只會得到未定義的 React 行為。 據我了解,react 只是對前一個和下一個 state 進行淺顯的比較以確定重新渲染並為更復雜的比較提供實用程序,所以這應該沒問題吧?
它將比較您是否調用setState
並在必要時進行更新。 如果你不使用setState
,它甚至不會檢查。
直接對 state 的任何更改(沒有setState()
)= 改變 state。 在您的情況下,它是這一行:
this.state.closeListeners.push(cb);
正如@twharmon 提到的,您更改了 memory 中的值,但這不會觸發組件的render()
,但您的組件最終將從父組件更新,從而導致丑陋且難以調試的副作用。 使用解構賦值語法解決您的問題:
this.setState({
closeListeners: [...this.state.closeListeners, cb]
});
PS:解構也有助於保持你的代碼更干凈:
const Backdrop = ({ show, onClose }) => (
show ? <div onClick={onClose} className={classes.Backdrop}></div> : null
);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.