【问题标题】:One React Component. Two HOCs. How to make setState correctly update props?一个反应组件。两个 HOC。如何使 setState 正确更新道具?
【发布时间】:2017-08-28 19:52:27
【问题描述】:

给定一个标准的 compose 函数和一个 'div' 组件,你将如何编写这两个 HOC:

  • “div”元素一开始是一个 20 像素的绿色框,然后点击后变成一个 50 像素的蓝色框。
  • 关注点 - a:将状态与 props 合并,b:触发状态更改,由单独的 HOC 处理。
  • 更新程序 HOC 将状态映射到 props,并设置默认状态
  • 调度程序 HOC 接受一个函数以在单击时获取新状态

下面的示例用于获取一个绿色框,并正确触发处理程序。更新仅在 Dispatcher HOC 的状态下发生。 updater HOC 的状态保持不变,它的 props 也是如此。

我真的很想知道发生了什么。在 compose 中翻转两个 HOC 的顺序会导致无法设置处理程序。由于它们都合并在{...this.props} 中,这对我来说没有意义。猜猜我不明白多个 HOC 如何合并 props 和 state。

const HOCDispatcher = myFunc => BaseComponent => {
  return class Dispatcher extends React.Component {
    constructor(props,context){
      super(props,context);
      this.handlerFn = (event)=>{this.setState(myFunc)}
    }
    render(){
      return createElement(BaseComponent,{...this.props,onClick:this.handlerFn});
    }
  }
}

const HOCUpdater = defaultState => BaseComponent => {
  return class Updater extends React.Component {
    constructor(props,context){
      super(props,context);
      this.state = Object.assign({},defaultState,this.state);
    }
    render(){
      return createElement(BaseComponent,{...this.props,...this.state});
    }
  }
}

const MyComponent = compose(
  HOCDispatcher(()=>({
    style:{width:'50px',height:'50px',background:'blue'}
  })),
  HOCUpdater({
    style:{width:'20px',height:'20px',background:'green'}
  }),
)('div');

【问题讨论】:

    标签: reactjs higher-order-components


    【解决方案1】:

    如果您尝试以一种不太复杂的结构来简化或编译您的代码,您可以更好地理解它:

    MyComponent 的初始版本

    const MyComponent= class Dispatcher extends React.Component {
      constructor(props,context){
        super(props,context);
        this.handlerFn = (event)=>{this.setState({
          style:{width:'50px',height:'50px',background:'blue'}
        })}
      }
      render(){
        return <HOCUpdater onClick={this.handlerFn}/>
      }
    }
    

    HOCUpdater 也呈现为:

    class Updater extends React.Component {
      constructor(props,context){
        super(props,context);
        this.state = {
          style:{width:'20px',height:'20px',background:'green'}
        };
      }
      render(){
        return <div style:{width:'20px',height:'20px',background:'green'}/>;
      }
    }
    

    从而呈现绿色框。

    触发点击后

    const MyComponent= class Dispatcher extends React.Component {
      constructor(props,context){
        super(props,context);
        this.handlerFn = (event)=>{this.setState({
          style:{width:'50px',height:'50px',background:'blue'}
        })};
        this.state= {
          style:{width:'50px',height:'50px',background:'blue'}
        };
      }
      render(){
        return <HOCUpdater onClick={this.handlerFn}/>
      }
    }
    

    如果你注意渲染,它仍然是一样的,因为this.props 没有改变,它仍然是空的。因此,盒子的样式没有变化,而Dispatcher 的状态发生了变化!

    你看到哪里出错了吗?好吧,只需将Dispatcher 中的this.props 更改为this.state,您就会看到神奇的发生。

    但是等等,还有更多!

    如果你有这样一行代码会发生什么?

    createElement('div',{
      style:{width:'50px',height:'50px',background:'blue'},
      style:{width:'20px',height:'20px',background:'green'}
    });
    

    好吧,它仍然渲染第一个(蓝色框),但为避免这种情况,请尝试将 HOCUpdater 的渲染方法更改为:

    return createElement(BaseComponent,{...this.state});
    

    并添加一个componentWillReceiveProps 方法,这样您的HOCUpdater 将如下所示:

    const HOCUpdater = defaultState => BaseComponent => {
      return class Updater extends React.Component {
        constructor(props,context){
          super(props,context);
          this.state = Object.assign({},defaultState,this.state);
        }
        componentWillReceiveProps(nextProps){
          this.setState(nextProps);
        }
        render(){
          return createElement(BaseComponent,{...this.state});
        }
      }
    }
    

    【讨论】:

    • 感谢阿明的彻底回答!回复:“好吧,只需在 Dispatcher 中将 this.props 更改为 this.state,你就会看到奇迹发生了。”同意。这是否意味着调度程序现在也在将状态映射到道具?
    • 它可以将状态合并到调度程序中的道具中。但是,这就是我试图通过创建两个 HOC 来区分的问题。如果我了解现在发生的情况,props 会在渲染函数中的 HOC 之间复制,但 state 和构造函数上的其他属性不会。合并来自不同 HOC 的状态的唯一方法是将每个 HOC 的状态合并到 props 中。这符合你的理解吗?
    猜你喜欢
    • 2020-01-13
    • 2019-06-07
    • 1970-01-01
    • 1970-01-01
    • 2019-03-29
    • 1970-01-01
    • 2021-12-23
    • 1970-01-01
    • 2017-12-26
    相关资源
    最近更新 更多