当孩子改变上下文

时间:2016-06-24 15:18:12

标签: javascript reactjs redux

我有四个React组件:

  • 整体家长(App或类似的)
  • Header
  • 呼叫的App孩子
  • Profile
  • LogoutLink,由Profile
  • 调用

我正在使用Auth0实施用户身份验证/登录系统。当用户登录时(通过Header中的“登录”按钮),App会更改Context对象的User以包含从Auth0检索到的所有数据。然后,系统的任何需要它的用户数据都可以访问。

登录后,UI会自动更新(使用Context更改),以便Header现在显示“Hey there {name}”而不是“Login”。这也是指向Profile页面/组件的链接(使用React Router的<Link to="/profile></Link>元素)。

Profile页面上有一个LogoutLink。单击时,会将用户注销,然后返回主页。它还应自动更新UI以将Header中的消息从“Hey there {name}”更改为“Login”。这是通过再次Context更改完成的。但是,此功能实际上并不起作用 - 用户已成功注销,但要查看上面描述的更改,用户需要刷新整个页面。 this.context.user未更新并发送回ProfileHeader我知道这是因为Redux和它的单向数据流(即数据可以只有向下,而不是向上),但我需要找到解决方法。

以下是我的基本代码:

LogoutLink.js

export default class LogoutLink extends React.Component {

  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
    this.state = {
      user: null,
    };
  }

  static propTypes = {
    value: React.PropTypes.string,
  }

  static contextTypes = {
    user: React.PropTypes.object,
  } // get context so this.context is available to get initial user data (before logout)

  static childContextTypes = {
    user: React.PropTypes.object,
  }

  getChildContext() {
    return {user: this.state.user}
  } // these two are for updating context

  componentWillMount() {
    this.setState({ user: this.context.user });
  } // set the internal LogoutLink state

  onClick() {
    this.setState({ user: null }); // set the internal user state to null following logout
  }

  renderLogoutLink() {
    const {value} = this.props;
    const {user} = this.state;
    if (user != null) {
      return <Link to="/profile" onClick={this.onClick}>{value}</Link>
    } else {
      return <span>You're already logged out!</span>
    }
  }

  render() {
    return <span>{this.renderLogoutLink()}</span>
  }

}

Header.js

export default class Header extends React.Component { // eslint-disable-line react/prefer-stateless-function
  constructor(props) {
    super(props);
    this.showLock = this.showLock.bind(this); // lock is the Auth0 module responsible for Login, also passed down by context
  }

  static contextTypes = {
    user: React.PropTypes.object,
    lock: React.PropTypes.object,
  }

  showLock() {
    const {lock} = this.context;
    lock.show();
  }

  shouldComponentUpdate(nextProps, nextState, nextContext) {
    if (this.context.user == null && nextContext.user != null) {
      return true;
    } else if (this.context.user != null && nextContext.user == null) {
      return true;
    }
    return false;
  } // after the LogoutLink is clicked, this still returns false

  renderLoginButton() {
    const {user} = this.context;
    if (user) {
      const name = user.nickname;
      return <Link to="/profile">Hey there {name}!</Link>
    } else {
      return <button onClick={this.showLock}>Login</button>
    }
  }

  render() {
    return (
      <header>
        {this.renderLoginButton()}
      </header>
    );
  }
}

我正在关注有关Context的官方React文档并对其进行更新,请点击此处:https://facebook.github.io/react/docs/context.html

正如我所说,我知道为什么这不起作用:数据只能以一种方式发送。但我需要找到一种方法来使这项工作,说实话,我认为我已经盯着这个太久了,现在已经没有选择,我的大脑有点疲惫。

2 个答案:

答案 0 :(得分:1)

找到解决此问题的方法。在我设置User项初始上下文的App.js代码中,我在User上添加了一个方法,将User设置为null,然后通过应用程序向下渗透。在Logout链接中,它调用此方法。这是代码:

App.js

profile.logout = () => {
  this.setState({ profile: null });
}

getChildContext()方法然后根据此状态更改设置用户上下文:

getChildContext() {
  return {
    user: this.state.profile
  };
}

LogoutLink.js

onClick() {
  const {user} = this.context;
  user.logout(); 
}

答案 1 :(得分:0)

React&#34; context&#34;功能很有用,但也很复杂和古怪,这就是为什么在使用它时仍有许多注意事项。特别是,我相信从false返回shouldComponentUpdate的组件会导致较低的组件在上下文内容发生更改时不会更新。

我的建议是尽可能少地使用上下文,并且只针对更改的数据。将更改的数据(例如当前用户)放入Redux xtate。