使用异步初始化的prop值设置受控输入的初始状态

时间:2018-10-01 13:59:05

标签: reactjs firebase firebase-authentication

我有一个受控输入,该输入接受名为user的道具。该对象由其父组件传递给它,在此它用观察者 1 异步初始化。

在父组件中:

onAuthStateChanged() {
  this.unregisterAuthObserver = onAuthStateChanged((user) => {
    this.setState({
      isSignedIn: Boolean(user),
      user,
    });
  });
}

我想用user.displayName填充受控输入的初始状态。如下所示,它不应该是反模式的,因为状态仅取决于构造中的道具user

class ControlledInput extends Component {
  constructor(props) {
    super(props);

    const { user } = props;

    this.state = {
      displayName: user ? user.displayName : '',
    };
  }
}

当我刷新受控输入时,会出现以下问题:

  1. 由于观察者尚未触发,user的值为undefined。构造了组件,并将displayName分配给''
  2. 观察者触发,user被分配了一个复杂对象,React重新渲染了该组件。这不会更改组件的状态,并且displayName仍然是''

我坚持如何利用组件生命周期方法来实现这一目标。这种情况下有最佳实践吗?我应该依赖异步道具还是将观察者移动到受控组件中?

我已经考虑过使用componentDidUpdate()来确定何时分配user,但这感觉就像是在被黑客入侵。

  

使用此机会在组件更新后在DOM上进行操作。只要您将当前的道具与以前的道具进行比较,这也是一个进行网络请求的好地方(例如,如果道具未更改,则可能不需要网络请求)。


1 观察者是Firebase身份验证的一部分,但我不确定这与问题是否相关。

1 个答案:

答案 0 :(得分:0)

无需在构造函数中进行分配。您应该使用提供给render方法的道具,并遵循Lifting State Up pattern处理对父代状态的更新。然后,它将向下流到子组件。

请参见下面的示例。 “增量”按钮模拟异步调用。 ControlledInput是只读显示,而ControlledInput2允许对状态中反映的输入进行编辑。

class Parent extends React.Component {
  constructor(props) {
    super(props);

    this.handleClick = this.handleClick.bind(this);
    this.onInputChanged = this.onInputChanged.bind(this);

    this.state = {
      user,
      count: 1
    };
  }

  handleClick(e) {
    const { user, count } = this.state;

    this.setState({
      user: { ...user, displayName: `display ${count + 1}` },
      count: count + 1
    });
  }

  onInputChanged(name) {
    this.setState({
      user: Object.assign(user, { displayName: name })
    });
  }

  render() {
    const { user, count } = this.state;

    return (
      <div>
        <ControlledInput user={user} />
        <div>{user.displayName}</div>
        <div>Count: {count}</div>
        <button onClick={this.handleClick}>increment</button>
        <div>
          <ControlledInput2 onInputChanged={this.onInputChanged} user={user} />
        </div>
      </div>
    );
  }
}

const ControlledInput = ({ user }) => 
  <input type="text" value={user.displayName} />;

class ControlledInput2 extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.props.onInputChanged(e.target.value);
  }

  render() {
    const { user } = this.props;
    return (
      <input
        type="text"
        onChange={this.handleChange}
        value={user.displayName}
      />
    );
  }
}

const user = { displayName: undefined };

const props = { user };
const rootElement = document.getElementById("sample");
ReactDOM.render(<Parent {...props} />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.2/umd/react-dom.production.min.js"></script>
<div id="sample"></div>