为什么我的 React 组件渲染了两次?

IT技术 javascript reactjs firebase google-cloud-firestore
2021-04-14 09:26:23

我不知道为什么我的 React 组件渲染了两次。所以我从 params 中提取一个电话号码并将其保存到 state 以便我可以在 Firestore 中进行搜索。除了渲染两次之外,一切似乎都运行良好......第一个渲染电话号码和零点。它第二次呈现所有数据时正确显示。有人可以指导我解决问题。

class Update extends Component {
constructor(props) {
    super(props);
    const { match } = this.props;
    this.state = {
        phoneNumber: match.params.phoneNumber,
        points: 0,
        error: ''
    }
}

getPoints = () => {
    firebase.auth().onAuthStateChanged((user) => {
        if(user) {
            const docRef = database.collection('users').doc(user.uid).collection('customers').doc(this.state.phoneNumber);
            docRef.get().then((doc) => {
                if (doc.exists) {
                const points = doc.data().points;
                this.setState(() => ({ points }));
                console.log(points);
                } else {
                // doc.data() will be undefined in this case
                console.log("No such document!");
                const error = 'This phone number is not registered yet...'
                this.setState(() => ({ error }));
                }
                }).catch(function(error) {
                console.log("Error getting document:", error);
                });
        } else {
            history.push('/')
        }
    });
}

componentDidMount() {
    if(this.state.phoneNumber) {
        this.getPoints();
    } else {
        return null;
    }
}

render() {
    return (
        <div>
            <div>
                <p>{this.state.phoneNumber} has {this.state.points} points...</p>
                <p>Would you like to redeem or add points?</p>
            </div>
            <div>
                <button>Redeem Points</button>
                <button>Add Points</button>
            </div>
        </div>
    );
  }
}

export default Update;
5个回答

您正在以严格模式运行您的应用程序。转到 index.js 并注释严格模式标签。你会发现一个单一的渲染。

发生这种情况是 React.StrictMode 的一个有意特性。它只发生在开发模式中,应该有助于在渲染阶段发现意外的副作用。

从文档:

严格模式无法自动为您检测副作用,但它可以通过使它们更具确定性来帮助您发现它们。这是通过有意重复调用以下函数来完成的:...

^ 在本例中为render函数。

使用 React.StrictMode 时可能导致重新渲染的官方文档:

https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects

你能详细说明一下吗。
2021-05-22 09:26:23
也许添加指向相关文档/文章的链接
2021-05-22 09:26:23
不知道为什么,但这就是我的情况。删除了严格的案例,双重渲染停止了!现在我知道这不是错误,我将重新激活它
2021-06-17 09:26:23

React 在getPoints完成异步操作之前渲染组件

所以第一个render显示pointsis的初始状态0,然后componentDidMount被调用并触发异步操作。
当异步操作完成并更新状态时,另一个render用新数据触发。

如果需要,您可以显示一个加载器或一个指示符,表明数据正在被获取并且还没有准备好通过条件渲染来显示

只需添加另一个布尔键,例如isFetching,在调用服务器时将其设置为 true,并在接收到数据时将其设置为 false。

你的渲染看起来像这样:

  render() {
    const { isFetching } = this.state;
    return (
      <div>
        {isFetching ? (
          <div>Loading...</div>
        ) : (
          <div>
            <p>
              {this.state.phoneNumber} has {this.state.points} points...
            </p>
            <p>Would you like to redeem or add points?</p>
            <div>
              <button>Redeem Points</button>
              <button>Add Points</button>
            </div>
          </div>
        )}
      </div>
    );
  }

这是因为 React 严格模式代码。

从 ReactDOM.render 代码中移除 -> React.StrictMode。

将在每次重新渲染时渲染 2 次:

ReactDOM.render(
  <React.StrictMode>
<App />
  </React.StrictMode>,
  document.getElementById('root')
);

将渲染 1 次:

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

PS 没有冒犯那些说不要担心“重新渲染多少次”的人……如果您通过 setTimeout 每 1 秒运行一个永久的 API 获取代码,并且每次使用该 API 的费用为 0.01 美分, 每次重新渲染时,它都会触发 setTimeout 函数 2 次(这意味着每秒调用次数会增加一倍),这意味着 5 秒后,每个调用 API 都会同时运行 1000 多个 setTimeout,并且在 1 小时后,这个数字将变成一万亿+,所以如果你弄错了,成本将变得天文数字。这个问题是通过移除 React.StrictMode 来解决的,所以代码会 WAI。

React.StrictMode,使其渲染两次,这样我们就不会在以下位置放置副作用

constructor
componentWillMount (or UNSAFE_componentWillMount)
componentWillReceiveProps (or UNSAFE_componentWillReceiveProps)
componentWillUpdate (or UNSAFE_componentWillUpdate)
getDerivedStateFromProps
shouldComponentUpdate
render
setState updater functions (the first argument)

所有这些方法都被多次调用,因此避免在它们中产生副作用很重要。如果我们忽略这个原则,很可能会导致不一致的状态问题和内存泄漏。

React.StrictMode 不能立即发现副作用,但它可以通过故意调用两次某些关键函数来帮助我们找到它们。

这些功能是:

Class component constructor, render, and shouldComponentUpdate methods
Class component static getDerivedStateFromProps method
Function component bodies
State updater functions (the first argument to setState)
Functions passed to useState, useMemo, or useReducer

这种行为肯定会对性能产生一些影响,但我们不必担心,因为它只发生在开发中而不是生产中。
信用:https : //mariosfakiolas.com/blog/my-react-components-render-twice-and-drive-me-crazy/

React 使用其虚拟 dom 及其差异算法在内部监控和管理其渲染周期,因此您无需担心重新渲染的次数。让重新渲染由react管理。即使渲染函数被调用,如果其中没有props或状态更改,也有不会在 ui 上刷新的子组件。每个 setstate 函数调用都会通知 react 检查差异算法,并调用渲染函数。

所以在你的情况下,因为你在 getPoints 函数中定义了一个 setstate,它告诉 react 通过渲染函数重新运行差异过程。