【问题标题】:React update child's props after parent's state change在父母的状态改变后反应更新孩子的道具
【发布时间】:2018-05-02 01:44:45
【问题描述】:

我正在尝试将 Draft.js 的编辑器状态从编辑器组件传递到我自己的 Sidebar 组件。

使用最顶层组件Notes 我使用回调从CustomEditor 获取编辑器状态并将其设置为Notes 状态。然后我将该状态作为道具传递给Sidebar

问题是属性是在回调触发之前设置的。我在想setTimeout,但这似乎很粗糙。我知道UNSAFE_componentWillReceiveProps(),但文档不推荐它。这个用例有什么反应吗?

export class Notes extends Component {
  constructor(props) {
    super(props);
    this.getEditorState = this.getEditorState.bind(this)
    this.state = {
      editorState: "the placeholder data Sidebar should not have as a prop"
    };
  }

  getEditorState(state) {
    console.log(state)
    this.setState({editorState: state})
  }

  render() {
    return (
      <section id="Notes">
        <div id="editor-holder">
          <Sidebar currentEditorState={this.state.editorState}/>
          <div id="Editor">
            <FileHeader />
            <CustomEditor getState={this.getEditorState}/>
          </div>
        </div>
      </section>
    );
  }

}

export default Notes;

【问题讨论】:

    标签: reactjs react-props


    【解决方案1】:

    新的 Context API 是此类问题的解决方案。我花了一点时间来理解它,但我想出的是 editorStateSidebar 作为道具。

      export const NoteContext = React.createContext("placeholderEditorState");
    
      export class Notes extends Component {
        constructor(props) {
          super(props);
          this.getEditorState = this.getEditorState.bind(this)
          this.getFolderData = this.getFolderData.bind(this)
          this.state = {
            editorState: null,
            folderData: null
          };
        }
    
        getEditorState(state) {
          this.setState({editorState: state});
        }
    
        getFolderData(data) {
          this.setState({folderData : data})
        }
    
        render() {
          return (
            <section id="Notes">
              <TopBar />
              <div id="editor-holder">
    
                <NoteContext.Provider value={{editorState: this.state.editorState}} >
                  <NoteContext.Consumer>
                    {(context)=>{ return (
                      <Sidebar currentEditorState={context.editorState} getFolderData={this.getFolderData}/>
                    )}}
                  </NoteContext.Consumer>
                </NoteContext.Provider>
    
                  <div id="Editor">
    
                    <NoteContext.Provider value={{folderData: this.state.folderData}} >
                      <FileHeader />
                    </NoteContext.Provider>
    
                    <CustomEditor getState={this.getEditorState}/>
                  </div>
    
              </div>
            </section>
          ); 
      }
    }
    

    现在看起来很简单,说明我学到了很多东西!让我知道我是否可以在这里改进。

    【讨论】:

      【解决方案2】:

      那么有更多可能的选择如何实现这个结果

      条件渲染

      只有当 props 改变了那个menas时,你才能渲染&lt;Sidebar&gt;

      constructor(props)
        super(props)
        this.state = {
          editorState: false
        }
      }
      
      render() {
       ... {this.state.editorState && <Sidebar currentEditorState={this.state.editorState}/>}
      }
      

      未定义/错误道具的保护组件

      Sidebar.js

      render() {
        if(!this.props.currentEditorState) return null // this will force React to render nothing
      
        return ( ... )
      }
      

      使用 getDerivedState 将道具转换为状态

      https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops

      Sidebar.js

      static getDerivedStateFromProps({ currentEditorState }, prevState) {
        if(currentEditorState !== false {
         return { currentEditorState }
        } else {
         return {}
        }
      }
      
      render() {
        (.... something bound to this.state.currentEditorState)
      }
      

      使用上下文(遗留上下文)

      class Notes extends React.Component {
       getEditorState(state) {
          console.log(state)
          this.setState({editorState: state})
        }
      
        getChildContext() {
          return {
            editorState: this.state.editorState
          }
        }
      
        childContextTypes = {
          editorState: PropTypes.oneOfType([PropTypes.obj, PropTypes.bool])
        }
      }
      

      Sidebar.js

      class Sidebar {
        static contextTypes = {
          editorState: PropTypes.oneOfType([PropTypes.obj, PropTypes.bool])
        }
      
        render() {
          ... this.context.editorState
        }
      }
      

      【讨论】:

      • 我是否正确地假设我的用例有些独特呢?您认为有更好的方法来设计我的数据流吗?
      • 你的情况是完全正常的,通常是用道具postponed 渲染孩子,例如当父组件充当数据加载器并向下传递道具时。完成相同的流程,例如在模态中,您有将 state.isOpen 设置为 true 的按钮,并且子模态组件打开。然而,将状态从一个组件传递到另一个组件看起来像是反模式。我会考虑将集中式存储作为 redux/mobx,或者如果你不想使用它,你可以使用 React context 自动共享状态。
      • 我将研究context 作为解决方案。这似乎是 React 对这种情况的解决方案。
      • 根据我阅读的文档,您使用的是旧的上下文 API。那是因为旧的上下文 API 最适合这种情况吗?我可以理解,对于这个小问题,新的 API 看起来需要做很多工作。见reactjs.org/docs/legacy-context.html#updating-context
      • 这是“未来”的遗产。 30 天前推出了新的,我没有在生产中进行测试,因此我不想发布我不熟悉的代码,但可以随意使用新的 context of course
      猜你喜欢
      • 2018-02-08
      • 1970-01-01
      • 2016-12-18
      • 2016-08-22
      • 2019-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-10
      相关资源
      最近更新 更多