如何强制更新一个React组件(附代码)

4,739 阅读4分钟

简介

在这篇文章中,我们将告诉你如何在React.js中强制更新一个组件。更具体地说,我们将简要介绍React re-renders,我们将展示如何在基于类的组件中强制更新,以及如何在功能组件中强制更新。

React重渲染

在大多数情况下,React本身会自动为你处理重新渲染组件的问题。其原因可能是基于道具或状态被更新的时间。所以当状态或属性发生变化时,组件会重新渲染。但如果你的组件依赖于其他东西,而不一定依赖于你的状态或属性呢?在这种情况下,你可能需要强制更新组件,因为React可能没有检测到这个变化。

让我们来看看如何在React组件上使用这种强制更新。为了展示这一点,我们将创建一个简单的应用程序用于演示。

注意:我们将涵盖React的一些概念,所以建议有React的基本知识。

强制更新基于类的组件

类组件有一个内置的方法来重新渲染一个组件,叫做forceUpdate() ,用来强制一个组件重新渲染。你可以在React的官方网站上阅读更多关于forceUpdate() 的方法。

handleForceupdateMethod() {
    // Force a render without state change...
    this.forceUpdate();
}

注意:不建议依赖使用forceUpdate() 方法来更新组件。当你发现自己需要这个方法时,你应该首先尝试分析你的代码,并找出是否有其他原因导致React没有更新组件。你可能会发现是一个bug造成的,或者你可以以一种方式重组你的代码,让React能够正确地自行重新渲染组件。

下面是一个如何在基于类的组件上强制更新的例子:

import React from 'react'

class App extends React.Component {
    constructor() {
        super();
        this.handleForceupdateMethod = this.handleForceupdateMethod.bind(this);
    };

    handleForceupdateMethod() {
        this.forceUpdate();
    };

    render() {
        return (
            <div>
                <h1>Hello StackAbuse</h1>
                <h3>Random Number: { Math.random() }</h3>
                <button onClick={this.handleForceupdateMethod}>
                    Force re-render
                </button>
            </div>
        );
    }
}

export default App

在这个方法中,有很多事情比它看起来要多。例如,调用forceUpdate() ,也会触发子组件的生命周期方法。正如我们所知道的React,只有当标记实际改变时,它才会更新DOM。

强制更新功能组件

功能性组件没有内置的方法可以像基于类的组件那样对其进行重新调整。这意味着我们没有可用的forceUpdate() 方法。然而,回顾一下,在React中,组件通常会因为状态或道具的变化而重新渲染。利用这一点,我们可以实现强制更新的方法。

从16.8版本开始,React有一个叫做Hooks的概念,可以在功能组件中用于更新状态,执行副作用等。我们将利用这些钩子的优势,让一个组件重新渲染。

下面是一些如何在一个功能组件中强制更新的例子。

使用useReducer 钩子

const [ignored, forceUpdate] = useReducer(x => x + 1, 0);

function handleClick() {
    forceUpdate();
}

React中的reducer通常在你有复杂的状态逻辑和动作时使用。在这里,我们只是通过更新一个假的状态变量x ,来触发更新。为了触发更新,状态必须实际发生变化,这就是为什么每次调用时都会递增。

使用useState 钩子

import React, { useState } from "react";

// Create your forceUpdate hook
function useForceUpdate() {
    let [value, setState] = useState(true);
    return () => setState(!value);
}

export default function App() {
    // Call your hook here
    const handleForceupdateMethod = useForceUpdate();

    return (
        <div className="App">
            <h1>Hello StackAbuse </h1>
            <h3>Random Number: { Math.random() }</h3>

            {/*
                Clicking on the button will force to re-render like force update does
            */}
            <button onClick={handleForceupdateMethod}>Force re-render</button>
        </div>
    );
}

这种类型的强制更新的想法与useReducer 非常相似,我们不断地更新状态来强制改变。在这里,我们不是像上一个方法那样递增一个计数器,而是简单地切换一个布尔值,以便在每次调用时将其否定。

使用useStateuseCallback 钩子

import React, { useState , useCallback} from "react";

export default function App() {
    const [, updateState] = useState();
    const handleForceupdateMethod = useCallback(() => updateState({}), []);

    // Check your console
    console.log("Rendering...");

    return (
        <div className="App">
            <h1>Hello StackAbuse</h1>
            <h3>Random Number: { Math.random() }</h3>

            {/*
                Clicking on the button will force to re-render like force update does
            */}
            <button onClick={handleForceupdateMethod}>Force re-render</button>
        </div>
    );
}

同样,这个策略通过改变状态来工作。在这种情况下,虽然我们在技术上没有改变状态的,但我们是在向它发送一个新的对象,这被React认为是新的,因为它没有对状态进行 "深入 "的平等检查。

正如你所看到的,这里有很多方法可以实现同样的事情。请记住,这些在技术上都是反模式,应该尽可能避免。但如果你无法解决根本问题,需要强制更新一个组件,那么我们在这里展示的任何方法都应该是有效的。

总结

在这篇文章中,我们看到了如何对React组件进行强制更新。我们也看到了如何在基于功能和类的组件中实现这一点。虽然不一定是好的做法,但了解它是如何工作的,以备我们需要在特殊情况下使用它是很有用的。