React构造函数仅对一次渲染两次的相同组件调用一次

时间:2018-09-19 08:03:59

标签: reactjs

我希望此切换有效,但是以某种方式只能调用组件<A/>的构造函数。 https://codesandbox.io/s/jvr720mz75

import React, { Component } from "react";
import ReactDOM from "react-dom";

class App extends Component {
  state = { toggle: false };
  render() {
    const { toggle } = this.state;
    return (
      <div>
        {toggle ? <A prop={"A"} /> : <A prop={"B"} />}
        <button onClick={() => this.setState({ toggle: !toggle })}>
          toggle
        </button>
      </div>
    );
  }
}

class A extends Component {
  constructor(props) {
    super(props);
    console.log("INIT");
    this.state = { content: props.prop };
  }

  render() {
    const { content } = this.state;
    return <div>{content}</div>;
  }
}

ReactDOM.render(<App />, document.getElementById("root"));

我已经找到解决方法https://codesandbox.io/s/0qmnjow1jw

<div style={{ display: toggle ? "none" : "block" }}>
  <A prop={"A"} />
</div>
<div style={{ display: toggle ? "block" : "none" }}>
  <A prop={"B"} />
</div>

我想了解为什么上面的代码不起作用

3 个答案:

答案 0 :(得分:3)

在反应中,如果您想多次渲染相同的组件并将它们视为不同,则需要为其提供唯一的key。尝试下面的代码。

{toggle ? <A key="A" prop={"A"} /> : <A key="B" prop={"B"} />}

答案 1 :(得分:2)

Since that ternary statement renders results in an <A> component in either case, when the <App>'s state updates and changes toggle, React sees that there is still an <A> in the same place as before, but with a different prop prop. When React re-renders it does so by making as few changes as possible. So since this is the same class of element in the same place, React doesn't need to create a new element when toggle changes, only update the props of that <A> element.

Essentially, the line

{toggle ? <A prop="A"/> : <A prop="B"/> }

is equivalent to

<A prop={ toggle ? "A" : "B" }/>

which perhaps more clearly does not need to create a new <A> component, only update the existing one.

The problem then becomes that you set the state.content of the <A> using props.prop in the constructor, so the state.content is never updated. The cleanest way to fix this would be to use props.prop in the render method of the <A> component instead of state.content. So your A class would look like this:

class A extends Component {
    render() {
        const { prop } = this.props;
        return <div>{ prop }</div>;
    }
}

If you must take the prop prop and use it in the <A> component's state, you can use componentDidUpdate. Here's an example:

class A extends Component {
    constructor(props) {
        super(props);
        this.state = {content: props.prop};
    }

    componentDidUpdate(prevProps) {
        if (prevProps.prop !== this.props.prop) {
            this.setState({content: this.props.prop});
        }
    }

    render() {
        const { content } = this.state;
        return <div>{ content }</div>
    }
}

答案 2 :(得分:1)

React will only call the constructor once. That's the expected outcome. Looks like you're trying to update the state of the component A based on the props. You could either use the prop directly or use the componentDidUpdate lifecycle method, as Henry suggested. Another way is using the static method getDerivedStateFromProps to update the state based on the prop passed.

static getDerivedStateFromProps(props, state) {
    return ({
        content: props.prop
    });
}