Reactjs 什么';将表单元素状态传递给同级/父元素的正确方法是什么? 假设我有一个React类P,它呈现两个子类C1和C2 C1包含一个输入字段。我将把这个输入字段称为Foo 我的目标是让C2对Foo的变化做出反应

Reactjs 什么';将表单元素状态传递给同级/父元素的正确方法是什么? 假设我有一个React类P,它呈现两个子类C1和C2 C1包含一个输入字段。我将把这个输入字段称为Foo 我的目标是让C2对Foo的变化做出反应,reactjs,Reactjs,我提出了两个解决方案,但两个都不太合适 第一个解决方案: 为P分配一个状态,状态。输入 在P中创建一个onChange函数,它接收一个事件并设置状态。input 将此onChange作为props传递给C1,并让C1将this.props.onChange绑定到Foo的onChange 这很有效。每当Foo的值改变时,它就会触发P中的setState,因此P将有输入传递给C2 但出于同样的原因,它感觉不太正确:我正在从子元素设置父元素的状态。这似乎违背了React的设计原则:单向数据流。 我应该

我提出了两个解决方案,但两个都不太合适

第一个解决方案:

  • 为P分配一个状态,
    状态。输入
  • 在P中创建一个
    onChange
    函数,它接收一个事件并设置
    状态。input
  • 将此
    onChange
    作为
    props
    传递给C1,并让C1将
    this.props.onChange
    绑定到Foo的
    onChange
  • 这很有效。每当Foo的值改变时,它就会触发P中的
    setState
    ,因此P将有输入传递给C2

    但出于同样的原因,它感觉不太正确:我正在从子元素设置父元素的状态。这似乎违背了React的设计原则:单向数据流。
    我应该这样做,还是有更具反应性的天然溶液?

    第二种解决方案:

    把福放在p

    但这是我在构建应用程序时应该遵循的设计原则吗?将所有表单元素放在最高级别的
    渲染中?

    就像在我的例子中,如果我有一个大的C1渲染,我真的不想把整个C1的
    render
    放到p的
    render
    ,仅仅因为C1有一个表单元素


    我应该怎么做呢?

    所以,如果我理解正确,您的第一个解决方案是建议您在根组件中保持状态?我不能代表React的创建者说话,但总的来说,我觉得这是一个合适的解决方案

    保持状态是创建React的原因之一(至少我认为是这样)。如果您曾经实现过自己的状态模式客户端,用于处理具有许多相互依赖的移动部件的动态UI,那么您会喜欢React,因为它减轻了很多这种状态管理的痛苦

    通过在层次结构中进一步保持状态,并通过事件进行更新,您的数据流仍然是单向的,您只是对根组件中的事件进行响应,您并不是通过双向绑定在那里获取数据,而是告诉根组件“嘿,这里发生了一些事情,请检查这些值”或者您正在向上传递子组件中某些数据的状态,以便更新状态。您更改了C1中的状态,并且希望C2知道它,因此,通过更新根组件中的状态并重新渲染,C2的道具现在是同步的,因为状态在根组件中更新并传递

    类示例扩展了React.Component{
    建造师(道具){
    超级(道具)
    this.state={data:'test'}
    }
    渲染(){
    返回(
    )
    }
    onUpdate(data){this.setState({data})}
    }
    类C1扩展了React.Component{
    渲染(){
    返回(
    )
    }
    更新(){
    this.props.onUpdate(this.refs.myInput.getDOMNode().value)
    }
    })
    C2类扩展了反应组件{
    渲染(){
    返回{this.props.data}
    }
    })
    ReactDOM.renderComponent(,document.body)
    
    现在我已经使用React构建了一个应用程序,我想就半年前我提出的这个问题分享一些想法

    我建议你读书

    第一篇文章对于理解如何构建React应用程序非常有帮助

    Flux回答了这样一个问题:为什么要这样构造React应用程序(而不是如何构造它)。React只占系统的50%,通过通量,你可以看到整个画面,看到它们如何构成一个连贯的系统

    回到问题上来

    至于我的第一个解决方案,完全可以让处理程序反向运行,因为数据仍然是单向运行的

    然而,让处理程序触发p中的setState可能是对的,也可能是错的,这取决于您的情况

    如果应用程序是一个简单的标记转换器,C1是原始输入,C2是HTML输出,那么让C1在p中触发setState是可以的,但有些人可能会认为这不是推荐的方法

    但是,如果应用程序是一个todo列表,C1是用于创建新todo的输入,C2是HTML中的todo列表,那么您可能希望处理程序比p高出两级——到
    调度程序
    ,它让
    存储更新
    数据存储,然后将数据发送到p并填充视图。看那篇文章。以下是一个例子:


    通常,我更喜欢todo列表示例中描述的方式。应用程序中的状态越少越好

    您应该学习Redux和ReactRedux库。它将在一个存储中构建您的状态和道具,您可以稍后在组件中访问它们

  • 正确的做法是在父组件中具有状态,以避免ref和其他不存在的情况
  • 一个问题是避免在字段中键入时不断更新所有子项
  • 因此,每个子级都应该是一个组件(而不是PureComponent),并实现
    shouldComponentUpdate(nextrops,nextState)
  • 这样,在表单字段中键入时,只有该字段更新
  • 下面的代码使用了ES中的
    @bound
    注释。接下来
    巴别塔插件转换装饰器巴别塔JS 6的遗留
    和类属性(注释在类似于绑定的成员函数上设置此值):

    /*
    ©2017年至今Harald Rudell(http://www.haraldrudell.com)
    版权所有。
    */
    导入反应,{Com
    
    class P extends React.Component {
        state = { foo : "" };
    
        render(){
            const { foo } = this.state;
    
            return (
                <div>
                    <C1 value={ foo } onChange={ x => this.setState({ foo : x })} />
                    <C2 value={ foo } />
                </div>
            )
        }
    }
    
    const C1 = ({ value, onChange }) => (
        <input type="text"
               value={ value }
               onChange={ e => onChange( e.target.value ) } />
    );
    
    const C2 = ({ value }) => (
        <div>Reacting on value change: { value }</div>
    );
    
    import React, { Component } from 'react';
    
    export default class P extends React.Component {
       constructor (props) {
          super(props)
          this.state = {data: 'test' }
          this.onUpdate = this.onUpdate.bind(this)
          this.ref = React.createRef();
       }
    
       onUpdate(data) {
          this.setState({data : this.ref.current.value}) 
       }
    
       render () {
          return (
            <div>
               <C1 ref={this.ref} onUpdate={this.onUpdate}/>
               <C2 data={this.state.data}/>
            </div>
          )
       }
    }
    
    const C1 = React.forwardRef((props, ref) => (
        <div>
            <input type='text' ref={ref} onChange={props.onUpdate} />
        </div>
    ));
    
    class C2 extends React.Component {
        render () {
           return <div>C2 reacts : {this.props.data}</div>
        }
    }
    
    import React, { useState, useContext } from "react";
    import ReactDOM from "react-dom";
    import styles from "./styles.css";
    
    // Create context container in a global scope so it can be visible by every component
    const ContextContainer = React.createContext(null);
    
    const initialAppState = {
      selected: "Nothing"
    };
    
    function App() {
      // The app has a state variable and update handler
      const [appState, updateAppState] = useState(initialAppState);
    
      return (
        <div>
          <h1>Passing state between components</h1>
    
          {/* 
              This is a context provider. We wrap in it any children that might want to access
              App's variables.
              In 'value' you can pass as many objects, functions as you want. 
               We wanna share appState and its handler with child components,           
           */}
          <ContextContainer.Provider value={{ appState, updateAppState }}>
            {/* Here we load some child components */}
            <Book title="GoT" price="10" />
            <DebugNotice />
          </ContextContainer.Provider>
        </div>
      );
    }
    
    // Child component Book
    function Book(props) {
      // Inside the child component you can import whatever the context provider allows.
      // Earlier we passed value={{ appState, updateAppState }}
      // In this child we need the appState and the update handler
      const { appState, updateAppState } = useContext(ContextContainer);
    
      function handleCommentChange(e) {
        //Here on button click we call updateAppState as we would normally do in the App
        // It adds/updates comment property with input value to the appState
        updateAppState({ ...appState, comment: e.target.value });
      }
    
      return (
        <div className="book">
          <h2>{props.title}</h2>
          <p>${props.price}</p>
          <input
            type="text"
            //Controlled Component. Value is reverse vound the value of the variable in state
            value={appState.comment}
            onChange={handleCommentChange}
          />
          <br />
          <button
            type="button"
            // Here on button click we call updateAppState as we would normally do in the app
            onClick={() => updateAppState({ ...appState, selected: props.title })}
          >
            Select This Book
          </button>
        </div>
      );
    }
    
    // Just another child component
    function DebugNotice() {
      // Inside the child component you can import whatever the context provider allows.
      // Earlier we passed value={{ appState, updateAppState }}
      // but in this child we only need the appState to display its value
      const { appState } = useContext(ContextContainer);
    
      /* Here we pretty print the current state of the appState  */
      return (
        <div className="state">
          <h2>appState</h2>
          <pre>{JSON.stringify(appState, null, 2)}</pre>
        </div>
      );
    }
    
    const rootElement = document.body;
    ReactDOM.render(<App />, rootElement);