【问题标题】:Setting the initial state of a controlled input with a prop value that is initialized asynchronously使用异步初始化的 prop 值设置受控输入的初始状态
【发布时间】:2019-03-06 15:54:53
【问题描述】:

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

在父组件中:

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

我想用user.displayName 填充受控输入的初始状态。像下面这样的东西,这不应该是一个反模式,因为状态只依赖于 prop 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 进行操作。只要您将当前的 props 与以前的 props 进行比较,这也是进行网络请求的好地方(例如,如果 props 没有更改,则可能不需要网络请求)。


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

【问题讨论】:

  • 为什么不在OnAuthStateChanged 的状态中添加displayName: user ? user.displayName : '',setState?
  • 你能发布你的渲染函数(实际上是整个组件)吗?您如何将更改传达给 userdisplayName
  • @ColinYoung 这是使用 React Router 中 Route 组件的包装器完成的:<ProtectedRoute path="/controlledInput" render={() => <ControlledInput user={user} />} ... />
  • @JamesPoag 我不确定这会解决我遇到的问题。在构造受控输入时,displayName 仍为空。

标签: reactjs firebase firebase-authentication


【解决方案1】:

无需在构造函数中赋值。您应该使用提供给 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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-29
    • 1970-01-01
    • 2019-05-08
    • 1970-01-01
    • 2018-06-10
    • 1970-01-01
    • 2019-10-30
    • 1970-01-01
    相关资源
    最近更新 更多