为什么React会一遍又一遍地以不变的状态渲染我的组件?

时间:2018-09-20 10:25:32

标签: javascript reactjs

我正在制作一个简单的从上到下的组件,我认为React仅在组件发生更改时才会重新渲染该组件。由于我的渲染中有条件绑定到状态,因此React是否应该仅在状态更改时才渲染它?取而代之的是,我看到它在每次滚动时都重新呈现。

此外,如果我将其保留原样,那么重新渲染有什么弊端吗?

import React from 'react';
import './scroll-to-top.css';

export default class extends React.Component {

  state = {
    shouldShowButton: false
  }

  componentDidMount() {
    window.addEventListener('scroll', this.handleScroll);
  }

  componentWillUnmount() {
    window.removeEventListener('scroll', this.handleScroll);
  }

  handleScroll = () => {
    this.setState({
    shouldShowButton: window.scrollY > 250 ? true : false
  });
 }

  render () {
    {console.log("i have rendered!")}
    return (
      this.state.shouldShowButton ? <a className="scroll-to-top" href="#">Return to Top</a> : null
    );
  };
};

3 个答案:

答案 0 :(得分:4)

欢迎堆栈溢出:)

让我们仔细考虑一下您的代码。

在加载组件时,您会将侦听器附加到滚动事件:

componentDidMount() {
  window.addEventListener('scroll', this.handleScroll);
}

当用户滚动时会触发handleScrollhandleScroll设置组件的状态,无论三元条件是否解析为true或false:

handleScroll = () => {
  this.setState({
    shouldShowButton: window.scrollY > 250 ? true : false
  });
}

每当我们使用setState时,React就会触发render。因此,render每次滚动都会触发。

缺点-将任何内容附加到scroll时应格外小心,因为它会影响性能。如果确实需要,可以考虑取消事件的弹跳。 (去抖动是限制函数可调用次数的技术。)

答案 1 :(得分:0)

之所以会发生这种情况,是因为每次触发滚动事件时都在调用handleScroll函数。要解决此问题,请仅在以下情况下使用setState:

import React from 'react';
import './scroll-to-top.css';

export default class extends React.Component {

  state = {
    shouldShowButton: false
  }

  componentDidMount() {
    window.addEventListener('scroll', this.handleScroll);
  }

  componentWillUnmount() {
    window.removeEventListener('scroll', this.handleScroll);
  }

  handleScroll = () => {
    const {shouldShowButton} = this.state;
    if (!shouldShowButton && window.scrollY > 250) {
       this.setState({
          shouldShowButton: true
       });
    } else if (shouldShowButton && window.scrollY <= 250) {
       this.setState({
          shouldShowButton: false
       });
    }
 }

  render () {
    {console.log("i have rendered!")}
    return (
      this.state.shouldShowButton ? <a className="scroll-to-top" href="#">Return to Top</a> : null
    );
  };
};

答案 2 :(得分:0)

否,这是典型的组件。每次调用.setState,更改props或重新渲染父元素时,都会重新渲染(不是在DOM中而是在虚拟DOM中)。 仅举一个例子,父母重新渲染还可以激发孩子的重新渲染:

import React from "react";
import ReactDOM from "react-dom";

class Child extends React.Component {
  render() {
    console.log('child re-rendered');
    return 'test';
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {a: 1};
    setInterval(() => this.setState(oldState => ({...oldState, a: oldState.a + 1})), 1000);
  }

  render() {
    return (
      <div className="App">
      <Child />
      </div>
    );
  }
}

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

在这里,您可以检查是否在调用父级.setState的行中重新渲染了孩子。

但这并不是100%的性能问题。虚拟DOM比浏览器DOM快得多。

但是,如果要避免这种行为,可以使用React.PureComponent而不是React.Component,这样它将不会在父级更新时重新呈现。当.setState实际上没有更改值时,PureComponent也可以处理大小写。 因此,重新渲染将更少。

官方文档足够好,但这里也是fine article at Medium