ReactJS:如何更改动态插入的Component数据

时间:2017-08-25 07:12:48

标签: reactjs

我试图通过使用状态将子组件更改为另一个组件。这会正确地注入新的组件,但是,如果我想动态更改其道具,则不会发生任何变化。 componentWillReceiveProps未被触发。

在我的场景中,我会有许多组件,如TestComponent(近20-30个组件),它们都有不同的HTML布局(它们也有子组件)。我通过从某个列表中选择一些值来在这些组件之间切换。

我认为最初加载所有这些组件并不是一个好主意。另一方面,我还没有发现任何关于在主Component中动态注入Component的事情。

这是我想要实现的一个非常基本的例子。点击按钮后,我在TestComponent内插入了App。之后,每隔一秒,我递增一个状态值,我尝试绑定TestComponent但是,组件值没有更新。

如果我在setInterval函数中使用注释片段而不是取消注释,它可以工作,但我必须编写20-30开关案例,以便在我的实际代码中找到正确的组件(我在从中选择一个值时也写过列表)所以,我想避免使用它。另外,我不确定性能。

那么,这是正确的方法,如果是这样,我该如何解决这个问题呢?如果错了,我还能尝试什么?



class App extends React.Component {
  constructor(props) {
    super(props);
    
    this.state = {
      component: <p>Initial div</p>,
      componentData: 0
    };
    
    this.onClickHandler = this.onClickHandler.bind(this);
  }
  
  onClickHandler = () => {
    this.setState({
      component: <TestComponent currentValue={this.state.componentData} />
    });
    
    setInterval(() => {
      this.setState({
        componentData: this.state.componentData + 1
      })
      
      // This will update TestComponent if used instead of above
      /*this.setState({
        componentData: this.state.componentData + 1,
        component: <TestComponent currentValue={this.state.componentData} />
      });*/
    }, 1000)
  }
  
  render() {
    return(
      <div>
        <h4>Click the button</h4>
        <button onClick={this.onClickHandler}>Change Component</button>
        {this.state.component}
      </div>
    )
  }
}

class TestComponent extends React.Component {
  constructor(props) {
    super(props);
    
    this.state = {
      currentValue: this.props.currentValue
    };
  }
  
  componentWillReceiveProps(nextProps) {
    this.setState({
      currentValue: nextProps.currentValue
    });
  }
  
  render() {
    return (
      <p>Current value: {this.state.currentValue}</p>
    )
  }
}

ReactDOM.render(
  <App />
  ,document.getElementById("app"));
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="app" style="width: 200px; height: 200px;"></div>
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:1)

要动态渲染子组件,可以在父级中使用React.createElement方法,这样可以调用不同的组件,这可以用作下面的示例代码,希望它有所帮助。

getChildComponent = (childComponentName) => {
        const childComponents = {
                TestComponent1,
                TestComponent2,
                TestComponent3,
                TestComponent4
            },
            componentProps = Object.assign({}, this.props,this.state, {
                styles: undefined
            });
        if (childComponents[childComponentName]) {
            return React.createElement(
                                        childComponents[childComponentName], 
                                        componentProps);
        }
        return null;
    }

render(){
    this.getChildComponents(this.state.childComponentName);
}

这里在render函数中传递组件名称,child将呈现dynalicaaly。其他方法可以是,将childComponents对象作为数组,查看下面的样本

const childComponents = [
                    TestComponent1,
                    TestComponent2,
                    TestComponent3,
                    TestComponent4
                ]
  

注意:您必须在父级中导入所有子组件,这些组件   不是字符串。

答案 1 :(得分:1)

那是因为Facebook在他们的React文档中提到过。

  

当您致电setState()时,React会将您提供的对象合并到当前状态。

     

合并很浅

     

了解更多信息read the documentation

因此,对于这种情况,唯一修改后的值为componentDatacomponent不会触发任何更新

解决方案

解决此问题的一个更好的案例是使用 Higher-Order components (HOC) ,因此App组件并不关心您尝试渲染的组件而是只接收一个组件作为道具,因此您可以在App状态下将道具传递给此组件。

此外,您不需要TestComponent中的州,因为您获得了作为道具的价值,并由App处理。

我还添加了一个条件来阻止添加多个setInterval

&#13;
&#13;
class App extends React.Component {
  interval;

  constructor(props) {
    super(props);
    
    this.state = {
      componentData: 0
    };

    this.onClickHandler = this.onClickHandler.bind(this);
  }
  
  onClickHandler = () => {
    if (!this.interval) {
      this.setState({
        componentData: this.state.componentData + 1
      });

      this.interval = setInterval(() => {
        this.setState({
          componentData: this.state.componentData + 1
        });
      }, 1000);
    }
  }
  
  render() {
    let Timer = this.props.timer;

    return(
      <div>
        <h4>Click the button</h4>
        <button onClick={this.onClickHandler}>Change Component</button>
        {!this.state.componentData ? <p>Initial div</p> : <Timer currentValue={this.state.componentData} />}
      </div>
    )
  }
}

class TestComponent extends React.Component {
  render() {
    const { currentValue } = this.props;

    return (
      <p>Current value: {currentValue}</p>
    )
  }
}

ReactDOM.render(<App timer={TestComponent} /> ,document.getElementById("app"));
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.js"></script>

<div id="app" style="width: 200px; height: 200px;"></div>
&#13;
&#13;
&#13;