2

编辑:查看 git 存储库以获取最小示例: https ://github.com/maximilianschmitt/blind-lifecycle

我有一个组件RequireUser试图确保用户已登录,否则不会呈现其子级。它的父组件App应该知道是否需要用户并在需要时呈现登录表单。

问题是,组件在App组件之后安装RequireUser在树中,如下所示:

App
  RequireUser
    SomeOtherComponent

RequireUser's中componentDidMount,我正在触发一个requireLoginUserStore'sloginRequired变量设置为true.

这不会更新父组件 ( App),因为它尚未安装,因此无法注册对存储的更改。

class RequireUser extends React.Component {
  constructor() {
    super();
    this.state = alt.stores.UserStore.getState();
  }

  componentDidMount() {
    this.unlisten = alt.stores.UserStore.listen(this.setState.bind(this));
    if (!this.state.requireUser) {
      UserActions.requireUser();
      // using setTimeout will work:
      // setTimeout(() => UserActions.requireUser());
    }
  }

  componentWillUnmount() {
    this.unlisten();
  }

  render() {
    if (this.state.requireUser) {
      return <div>I have required your user</div>;
    }

    return <div>I will require your user</div>;
  }
}

class App extends React.Component {
  constructor() {
    super();
    this.state = alt.stores.UserStore.getState();
  }

  componentDidMount() {
    this.unlisten = alt.stores.UserStore.listen(this.setState.bind(this));
  }

  componentWillUnmount() {
    this.unlisten();
  }

  render() {
    return (
      <div>
        <div>User required? {this.state.requireUser + ''}</div>
        <RequireUser />
      </div>
    );
  }
}

输出:

需要用户吗?错误的

我需要你的用户

如果我使用setTimeoutin RequireUserApp接收状态更改并呈现,但仅在闪烁之后:

需要用户吗?真的

我需要你的用户

我觉得我正在做的是一种反模式,我将不胜感激提供比使用 setTimeout 闪烁更优雅的解决方案的建议。谢谢!

4

3 回答 3

1

我建议的答案是将其添加到 App 组件中:

componentDidMount() {
    // setup listener for subsequent changes
    alt.stores.UserStore.listen(this.onChange);

    // grab the current state now that we're mounted
    var userStoreState = alt.stores.UserStore.getState();
    this.setState(userStoreState);
}

没有办法避免双重渲染。您的 RequireUser 组件已经执行了两次渲染。

  1. RequireUser 的初始渲染
  2. componentDidMount() 回调
    • 一个动作被派发
  3. UserStore 接收派发的动作并更新其状态
    • 发出更改通知
  4. RequireUser 根据状态变化设置状态
  5. RequireUser 的第二次渲染

但是你的代码库仍然被认为是 Flux,并且确实遵循了为 React 应用程序设计的模式。从本质上讲,您有一个加载状态......我们实际上不知道我们是否需要一个用户的状态。根据UserActions.requireUser()所做的事情,这可能需要也可能不需要。

你可以考虑重构

如果将 RequireUser 重写为仅查看组件,则可以修复双重渲染。这意味着没有侦听器,也没有在内部设置状态。该组件仅根据传入的道具呈现元素。这实际上是您的 RequireUser 组件的全部内容:

class RequireUser extends React.Component {
    render() {
        if (this.props.requireUser) {
            return <div>I have required your user</div>;
        }
        return <div>I will require your user</div>;
     }
}

然后,您将使您的 App 组件成为controller-view。这里添加了监听器,状态的任何变化都通过 props 向下传播。现在我们可以在 componentWillMount 回调中进行设置。这给了我们单一的渲染行为。

class App extends React.Component {
    (other lifecycle methods)

    componentWillMount() {
        if (!this.state.requireUser) {
            UserActions.requireUser();
        }
        var userStoreState = alt.stores.UserStore.getState();
        this.setState(userStoreState);
    }

    componentDidMount() {
        (same as above)
    }

    render() {
        return (
            <div>
                <div>User required? {this.state.requireUser + ''}</div>
                <RequireUser requireUser={this.state.requireUser} />
            </div>
       );
    }
}

Flux 架构和控制器视图/视图:https ://facebook.github.io/flux/docs/overview.html#views-and-controller-views

于 2015-11-05T17:24:44.567 回答
1

您的每个组件只能states从您的商店中获取一次- 仅在每个组件的构建期间。这意味着states您的组件中的 in 将不会states与in同步store

您需要在安装时在组件上设置一个store侦听器,以便从最新的store和最新的states. 用于setState()更新states组件内部,因此render()将再次调用以呈现最新的states

于 2015-11-05T17:33:19.113 回答
-1

将商店监听器放在构造函数中怎么样?这对我有用。

于 2015-11-05T20:12:03.053 回答