【问题标题】:React Component Not Updating After Changing A Value更改值后反应组件未更新
【发布时间】:2017-09-15 13:03:30
【问题描述】:

在 ReactJS 中,我正在编写一个无状态组件;
因为我已经阅读了避免不必要的状态是最佳实践。

该组件表示一个输入字段,当输入框包含一个值时执行一个函数。

    export const InputField = (props) => {
      
      const InputFieldContentsChanged = (event) => {
        props.onChange(event.target.value);
      };

      return (
        <div data-component="input-field"
          className={(props.value !== "" ? "value": "")}>
          <input type={props.type} value={props.value} onChange={InputFieldContentsChanged} />
          <span className="bar"></span>
          <label>{props.label}</label>
        </div>
      );
    };

    InputField.PropTypes = {
      type: PropTypes.oneOf([ "text", "password" ]).isRequired,
      label: PropTypes.string.isRequired,
      value: PropTypes.string,
      onChange: PropTypes.func.isRequired
    }

现在, 我创建了另一个组件,它只是用于测试上述组件的示例。 如下所示:

    export const SampleComponent = (props) => {
      
      let componentUsername = "";
      
      const onUsernameChanged = (username) => {
        componentUsername = username;
      };
      
      return (
        <InputField type="text" label="Username" value={componentUsername} onChange={onUsernameChanged} />
      );
    };

所以,我将value 绑定到组件中的自定义变量,当输入字段的内容发生更改时,该变量也会更改。

为什么输入框组件没有用新的用户名更新自己?

亲切的问候,

【问题讨论】:

  • 你能搞定一个小提琴并运行吗?
  • 无状态组件是无状态的。它们应该被实现为纯函数。返回值应仅取决于参数(道具)。由于没有任何东西改变道具 - 不会发生重新渲染。但是您的组件实际上并不是无状态的,它们确实具有可变的内部状态(输入值)。因此,您需要同时实现这两者或容器一个作为有状态组件。

标签: reactjs react-hooks react-state react-functional-component react-class-based-component


【解决方案1】:

功能组件的想法是不对状态或道具进行任何更改。

由于没有重新渲染组件的触发器,因此您不会看到任何变化。

将此React.Function 更改为React.Component

const InputField = (props) => {  
  const InputFieldContentsChanged = (event) => {
    console.log(event.target.value);
    props.onChange(event.target.value);
  };

  return (
    <div data-component="input-field"
      className={(props.value !== "" ? "value": "")}>
      <input type={props.type} value={props.value} onChange={InputFieldContentsChanged} />
      <span className="bar"></span>
      <label>{props.label}</label>
    </div>
  );
};

class SampleComponent extends React.Component {
  constructor() {
    super();
    this.state = { componentUsername : ""};
  }
  
  onUsernameChanged = (username) => {
    console.log(username);
    this.setState({componentUsername: username});
  }
  
  render() {
    return (
      <InputField type="text" label="Username" value={this.state.componentUsername} onChange={this.onUsernameChanged} />
    );
  }
};

ReactDOM.render(<SampleComponent/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

【讨论】:

    【解决方案2】:

    我正在编写一个无状态的 React 组件,因为最好的做法是在不需要时避免状态。

    在您的代码中,您尝试使用自己的“状态”,它只是一个变量 (componentUsername)。但由于它不是 React 状态,因此组件不会在变量更改时重新渲染。 React 根本不知道变化。

    因此,要么使用通常的setState 而不是重新分配您自己的“状态”变量,或者将逻辑放在父组件中并通过道具将componentUsername 传递给SampleComponent

    const SampleComponent = props => (
      <input type="text" onChange={props.onChange} value={props.value} />
    );
    
    class ParentComponent extends React.Component {
      constructor() {
        super();
        this.state = { value: '' };
        this.handleInputChange = this.handleInputChange.bind(this);
      }
    
      handleInputChange(e) {
        console.log(e.target.value);
        this.setState({ value: e.target.value });
      }
    
      render() {
        return (
          <SampleComponent
            value={this.state.value}
            onChange={this.handleInputChange}
          />
        );
      }
    }
    
    ReactDOM.render(<ParentComponent />, document.getElementById('root'));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <div id="root"></div>

    【讨论】:

    • 谢谢。如果我没听错的话,“top-root”组件应该有“state”才能触发重新渲染,对吗?
    • 另一件事,如果我将 componentUsername 作为属性分配给 InputField,那么由于组件无法更新它自己的 props,我该如何触发重新渲染?
    • 是的,没错!当然,像 Redux 或 Mobx 这样的状态容器也有例外。
    • 因为父组件更新了SampleComponent的props。 componentUsername 在这种情况下应该是父状态的一部分。
    • 好的,这就很清楚了。你能给我以下建议吗?我应该在父组件中维护状态,还是应该在 InputField 组件本身中使用内部状态?
    猜你喜欢
    • 2017-05-05
    • 2019-06-10
    • 2021-07-07
    • 2017-08-14
    • 1970-01-01
    • 2021-06-15
    • 2018-09-01
    相关资源
    最近更新 更多