React:更新子组件而不渲染父组件

时间:2018-08-25 14:01:06

标签: javascript reactjs

下面是一个简单的示例:

const { Component } = React
const { render } = ReactDOM

const Label = ({text}) => (<p>{text}</p>)

const Clock = ({ date }) => (
  <div>{date.toLocaleTimeString()}</div>
)

class App extends Component {
  
  constructor() {
    super()
    this.state = {
      date: new Date()
    }
  }
  
  componentWillMount() {
    this.interval = setInterval(
      () => this.setState({ date: new Date() }),
      1000
    )
  }
  
  componentWillUnmount() {
    clearInterval(this.interval)
  }
  
  updateTime() {
    
  }
  
  render() {
    return (
      <div>
        <Label text="The current time is:" />
        <Clock date={this.state.date} />
      </div>
    )
  }
  
}

render(<App />, document.querySelector('#app'))
<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"></div>

这是指向codepen

的链接

this.setState({ date: new Date() })每秒被调用一次,以当前时间更新Clock。据我所知,setState在App上调用render方法,这将导致整个组件(包括Label)都被重新渲染。

有没有一种方法可以将日期传递给Clock(使其重新呈现)而又不重新呈现整个App组件?在性能方面,这起了多大作用?

2 个答案:

答案 0 :(得分:2)

不可能是您想要的。要将prop传递给子组件,父组件的状态或prop应该以某种方式更改。如您所知,这显然会引发重新渲染,因此所有子级都将重新渲染。要进行更新,在这种情况下,应重新渲染Clock组件,并进行挂载/挂载,以反映DOM的变化。

如果您的应用程序不是很大,并且没有那么多孩子,则不必担心此问题,因为渲染并不那么昂贵。昂贵的一个是组件的DOM操作。在这里,React会比较实际和虚拟DOM,即使重新渲染也不会卸载/重新安装Label组件。但是,如果您将Label组件编写为PureComponent,则它不会重新呈现。但是要像这样更新Clock组件,就没有办法。

class Label extends React.PureComponent {
  render() {
    console.log("rendered");
    return (<p>{this.props.text}</p>)
  }
}

const Clock = ({ date }) => (
  <div>{date.toLocaleTimeString()}</div>
)

class App extends React.Component {

  constructor() {
    super()
    this.state = {
      date: new Date()
    }
  }


  componentWillMount() {
    this.interval = setInterval(
      () => this.setState({ date: new Date() }),
      1000
    )
  }

  componentWillUnmount() {
    clearInterval(this.interval)
  }

  updateTime() {

  }

  render() {
    return (
      <div>
        <Label text="The current time is:" />
        <Clock date={this.state.date} />
      </div>
    )
  }

}

答案 1 :(得分:0)

要更新子项而不更新父项时,状态必须在子项中。您可以将状态getter / setter从子级传递给父级,以便能够读取和更新它:

function Child({onMount}) {
  const [value, setValue] = useState(0);

  useEffect(() => {
    onMount([value, setValue]);
  }, [onMount, value]);


  return (
    <div>
      {value}
    </div>    
  );
};


function Parent() {

  let value = null;
  let setValue = null;
  
  const onChildMount = (dataFromChild) => {
    value = dataFromChild[0];
    setValue = dataFromChild[1];
  };

  // Call setValue to update child without updating parent

  return (
    <div>
      <Child onMount={onChildMount}/>
    </div>    
  );
};

由于const [value, setValue] = useState(0);位于Child中,因此在更新值时仅子组件会重新呈现。另外,由于Parentvalue收到setValueonChildMount,因此父级可以使用它们来更新子级,而无需重新呈现父级。