簡體   English   中英

在 React 中什么算作變異 state?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM