zoukankan      html  css  js  c++  java
  • 如何使用React Hooks将React类组件转换为功能组件

    介绍

    React的最新alpha版本引入了一个称为Hooks的新概念。钩子被引入到React中以解决常见问题。但是,它们主要用作类的替代方法。使用Hooks,可以创建使用状态和生命周期方法的功能组件。

    钩子目前在React v16.7.0-alpha中可用。没有删除类的计划。钩子提供了另一种编写React的方式。

    鉴于Hook仍然是新手,许多开发人员都希望将其概念应用到现有的React应用程序或新应用程序中。在本文中,您将探索使用React Hooks将React类组件转换为功能组件的五种方法。

    先决条件

    要完成本教程,您需要:

    不需要本地开发,但提供了CodeSandbox示例以供进一步实验。

    步骤1 —了解没有状态或生命周期方法的类

    让我们从一个既没有状态又没有生命周期组件的React类开始:

    ExampleClassComponent.js

    import React, { Component } from 'react';
    
    class App extends Component {
      alertName = () => {
        alert('John Doe');
      };
    
      render() {
        return (
          <div>
            <h3>This is a Class Component</h3>
            <button onClick={this.alertName}>
              Alert
            </button>
          </div>
        );
      }
    };
    
    export default App;
    

    复制

    在这里,您有一个典型的React类,该类缺少状态或生命周期方法。单击按钮时,它会提醒名称。

    该类的功能等效项如下所示:

    ExampleFunctionalComponent.js

    import React from 'react';
    
    function App() {
      const alertName = () => {
        alert('John Doe');
      };
    
      return (
        <div>
          <h3>This is a Functional Component</h3>
          <button onClick={alertName}>
            Alert
          </button>
        </div>
      );
    };
    
    export default App;
    

    复制

    像第一个示例一样,此功能类的行为也很典型。

    但是,此示例未使用Hooks或任何新内容。在这些示例中,您不需要状态或生命周期。

    让我们看一下带有状态的基于类的组件,并学习如何使用Hooks将它们转换为功能组件。

    第2步-将钩子添加到带有状态的类中

    让我们考虑一种情况,您有一个全局名称变量,可以在应用程序中从文本输入字段更新该变量。

    在React中,您可以通过在state对象中定义name变量并setState()在我们有一个新值来更新name变量时调用以下方法来处理这种情况:

    ExampleClassComponentWithState.js

    import React, { Component } from 'react';
    
    class App extends Component {
      state = {
        name: ''
      }
    
      alertName = () => {
        alert(this.state.name);
      };
    
      handleNameInput = e => {
        this.setState({ name: e.target.value });
      };
    
      render() {
        return (
          <div>
            <h3>This is a Class Component</h3>
            <input
              type="text"
              onChange={this.handleNameInput}
              value={this.state.name}
              placeholder="Your Name"
            />
            <button onClick={this.alertName}>
              Alert
            </button>
          </div>
        );
      }
    }
    
    export default App;
    

    复制

    当用户在输入字段中输入名称并单击“警报”按钮时,它将弹出一个警报,其中警报的状态已定义。

    您可以使用Hooks将整个类转换为功能性的React组件:

    ExampleFunctionalComponentWithState.js

    import React, { useState } from 'react';
    
    function App() {
      const [name, setName] = useState('John Doe');
    
      const alertName = () => {
        alert(name);
      };
    
      const handleNameInput = e => {
        setName(e.target.value);
      };
    
      return (
        <div>
          <h3>This is a Functional Component</h3>
          <input
            type="text"
            onChange={handleNameInput}
            value={name}
            placeholder="Your Name"
          />
          <button onClick={alertName}>
            Alert
          </button>
        </div>
      );
    };
    
    export default App;
    

    复制

    在这里,您已经介绍了useStateHook。它允许您在React功能组件中使用状态。通过useState()挂钩,您可以在此功能组件中使用状态。它使用类似的语法并为数组分配了解构分配。

    考虑这一行:

    const [name, setName] = useState('John Doe')
    

    复制

    在这里,name相当于this.state普通类组件中的,并且setName相当于this.setState

    useState()Hook中状态的初始值来自一个参数。换句话说,useState()参数是状态的初始值。您可以将其设置为'John Doe'。这意味着处于状态的名称的初始状态为'John Doe'

    这段代码是一个示例,说明如何使用Hooks将具有状态的基于类的React组件转换为功能组件。

    让我们探索其他场景,包括具有多个状态属性的类。

    步骤3 —将钩子添加到具有多个状态属性的类

    您已经研究了如何使用转换一个状态属性useState,但是当您具有多个状态属性时,相同的方法将行不通。如果,例如,你有两个或多个输入字段userNamefirstName以及lastName,那么你将有三个状态特性基于类的成分:

    ExampleClassComponentWithMultipleStateProperties.js

    import React, { Component } from 'react';
    
    class App extends Component {
      state = {
        userName: '',
        firstName: '',
        lastName: ''
      };
    
      logName = () => {
        console.log(this.state.userName);
        console.log(this.state.firstName);
        console.log(this.state.lastName);
      };
    
      handleUserNameInput = e => {
        this.setState({ userName: e.target.value });
      };
      handleFirstNameInput = e => {
        this.setState({ firstName: e.target.value });
      };
      handleLastNameInput = e => {
        this.setState({ lastName: e.target.value });
      };
    
      render() {
        return (
          <div>
            <h3>This is a Class Component</h3>
            <input
              type="text"
              onChange={this.handleUserNameInput}
              value={this.state.userName}
              placeholder="Your Username"
            />
            <input
              type="text"
              onChange={this.handleFirstNameInput}
              value={this.state.firstName}
              placeholder="Your First Name"
            />
            <input
              type="text"
              onChange={this.handleLastNameInput}
              value={this.state.lastName}
              placeholder="Your Last Name"
            />
            <button
              className="btn btn-large right"
              onClick={this.logName}
            >
              Log Names
            </button>
          </div>
        );
      }
    }
    
    export default App;
    

    复制

    要将此类转换为带有Hooks的功能组件,您将不得不采取一些非常规的方法。使用useState()挂钩,上面的示例可以写成:

    ExampleFunctionalComponentWithMultipleStateProperties.js

    import React, { useState } from 'react';
    
    function App() {
      const [userName, setUsername] = useState('');
      const [firstName, setFirstname] = useState('');
      const [lastName, setLastname] = useState('');
    
      const logName = () => {
        console.log(userName);
        console.log(firstName);
        console.log(lastName);
      };
    
      const handleUserNameInput = e => {
        setUsername(e.target.value);
      };
      const handleFirstNameInput = e => {
        setFirstname(e.target.value);
      };
      const handleLastNameInput = e => {
        setLastname(e.target.value);
      };
    
      return (
        <div>
          <h3>This is a Functional Component</h3>
          <input
            type="text"
            onChange={handleUserNameInput}
            value={userName}
            placeholder="Your Username"
          />
          <input
            type="text"
            onChange={handleFirstNameInput}
            value={firstName}
            placeholder="Your First Name"
          />
          <input
            type="text"
            onChange={handleLastNameInput}
            value={lastName}
            placeholder="Your Last Name"
          />
          <button
            className="btn btn-large right"
            onClick={logName}
          >
            Log Names
          </button>
        </div>
      );
    };
    
    export default App;
    

    复制

    这是此示例的CodeSandbox

    这演示了如何使用useState()Hook将具有多个状态属性的基于类的组件转换为功能组件。

    第4步-将钩子添加到带有State和 componentDidMount

    让我们考虑使用state和的类componentDidMount。为了进行演示,您将看到一个场景,其中您为三个输入字段设置了初始状态,并在五秒钟后将它们全部更新为一组不同的值。

    为此,您将为输入字段声明一个初始状态值,并实现一个componentDidMount()生命周期方法,该方法将在初始渲染之后运行以更新状态值:

    ExampleClassComponentWithStateAndComponentDidMount.js

    import React, { Component } from 'react';
    
    class App extends Component {
      state = {
        // initial state
        userName: 'johndoe',
        firstName: 'John',
        lastName: 'Doe'
      }
    
      componentDidMount() {
        setInterval(() => {
          this.setState({
            // update state
            userName: 'janedoe',
            firstName: 'Jane',
            lastName: 'Doe'
          });
        }, 5000);
      }
    
      logName = () => {
        console.log(this.state.userName);
        console.log(this.state.firstName);
        console.log(this.state.lastName);
      };
    
      handleUserNameInput = e => {
        this.setState({ userName: e.target.value });
      };
      handleFirstNameInput = e => {
        this.setState({ firstName: e.target.value });
      };
      handleLastNameInput = e => {
        this.setState({ lastName: e.target.value });
      };
    
      render() {
        return (
          <div>
            <h3>This is a Class Component</h3>
            <input
              type="text"
              onChange={this.handleUserNameInput}
              value={this.state.userName}
              placeholder="Your Username"
            />
            <input
              type="text"
              onChange={this.handleFirstNameInput}
              value={this.state.firstName}
              placeholder="Your First Name"
            />
            <input
              type="text"
              onChange={this.handleLastNameInput}
              value={this.state.lastName}
              placeholder="Your Last Name"
            />
            <button
              className="btn btn-large right"
              onClick={this.logName}
            >
              Log Names
            </button>
          </div>
        );
      }
    }
    
    export default App;
    

    复制

    当应用运行时,输入字段将具有您在状态对象中定义的初始值。componentDidMount()五秒钟后,这些值将更新为您在方法内部定义的值。

    接下来,您将使用ReactuseStateuseEffectHooks将此类转换为功能组件:

    ExampleFunctionalComponentWithStateAndComponentDidMount.js

    import React, { useState, useEffect } from 'react';
    
    function App() {
      const [userName, setUsername] = useState('johndoe');
      const [firstName, setFirstname] = useState('John');
      const [lastName, setLastname] = useState('Doe');
    
      useEffect(() => {
        setInterval(() => {
          setUsername('janedoe');
          setFirstname('Jane');
          setLastname('Doe');
        }, 5000);
      });
    
      const logName = () => {
        console.log(userName);
        console.log(firstName);
        console.log(lastName);
      };
    
      const handleUserNameInput = e => {
        setUsername({ userName: e.target.value });
      };
      const handleFirstNameInput = e => {
        setFirstname({ firstName: e.target.value });
      };
      const handleLastNameInput = e => {
        setLastname({ lastName: e.target.value });
      };
    
      return (
        <div>
          <h3>This is a Functional Component</h3>
          <input
            type="text"
            onChange={handleUserNameInput}
            value={userName}
            placeholder="Your Username"
          />
          <input
            type="text"
            onChange={handleFirstNameInput}
            value={firstName}
            placeholder="Your First Name"
          />
          <input
            type="text"
            onChange={handleLastNameInput}
            value={lastName}
            placeholder="Your Last Name"
          />
          <button
            className="btn btn-large right"
            onClick={logName}
          >
            Log Names
          </button>
        </div>
      );
    };
    
    export default App;
    

    复制

    这是此示例的CodeSandbox

    在功能方面,此组件的作用与上一个示例完全相同。唯一的区别是,您没有使用在类组件中使用的常规state对象和componentDidMount()生命周期方法,而是使用了useStateuseEffect挂钩。

    第5步-添加鱼钩与国家一类,componentDidMountcomponentDidUpdate

    接下来,让我们看一下带有状态和两个生命周期方法的React类:componentDidMountcomponentDidUpdate。到目前为止,大多数解决方案都使用了useStateHook。在此示例中,您将专注于useEffect挂钩。

    为了最好地演示它是如何工作的,让我们修改代码以动态更新<h3>页面上的标题。

    当前,标题显示This is a Class Component。现在,您将定义一种componentDidMount()方法来更新标头,使其Welcome to React Hooks在三秒钟后显示:

    ExampleClassComponentWithStateAndTwoLifecycleMethods.js

    import React, { Component } from 'react';
    
    class App extends Component {
      state = {
        header: 'Welcome to React Hooks'
      }
    
      componentDidMount() {
        const header = document.querySelectorAll('#header')[0];
        setTimeout(() => {
          header.innerHTML = this.state.header;
        }, 3000);
      }
    
      render() {
        return (
          <div>
            <h3 id="header">This is a Class Component</h3>
          </div>
        );
      }
    }
    
    export default App;
    

    复制

    应用运行时,它将以初始标题开始,This is a Class ComponentWelcome to React Hooks在三秒钟后更改为。这是经典componentDidMount()行为,因为它会在render函数成功执行后运行。

    让我们添加功能来动态更新另一个输入字段中的标题,以便在键入时使用新文本更新标题。

    为此,您将需要实现componentDidUpdate()生命周期方法:

    ExampleClassComponent.js

    import React, { Component } from 'react';
    
    class App extends Component {
      state = {
        header: 'Welcome to React Hooks'
      }
    
      componentDidMount() {
        const header = document.querySelectorAll('#header')[0];
        setTimeout(() => {
          header.innerHTML = this.state.header;
        }, 3000);
      }
    
      componentDidUpdate() {
        const node = document.querySelectorAll('#header')[0];
        node.innerHTML = this.state.header;
      }
    
      handleHeaderInput = e => {
        this.setState({ header: e.target.value });
      };
    
      render() {
        return (
          <div>
            <h3 id="header">This is a Class Component</h3>
            <input
              type="text"
              onChange={this.handleHeaderInput}
              value={this.state.header}
            />
          </div>
        );
      }
    }
    
    export default App;
    

    复制

    在这里,你有statecomponentDidMount()componentDidUpdate()。当您运行该应用程序时,该componentDidMount()功能会将标头更新为Welcome to React Hooks三秒钟。当您开始在标题文本输入字段中输入内容时,<h3>文本将使用componentDidUpdate()方法中定义的输入文本进行更新。

    接下来,您将使用useEffect()Hook将此类转换为功能组件:

    ExampleFunctionalComponentWithStateAndTwoLifecycleMethods.js

    import React, { useState, useEffect } from 'react';
    
    function App() {
      const [header, setHeader] = useState('Welcome to React Hooks');
    
      useEffect(() => {
        const newheader = document.querySelectorAll('#header')[0];
        setTimeout(() => {
          newheader.innerHTML = header;
        }, 3000);
      });
    
      const handleHeaderInput = e => {
        setHeader(e.target.value);
      };
    
      return (
        <div>
          <h3 id="header">This is a Functional Component</h3>
          <input
            type="text"
            onChange={handleHeaderInput}
            value={header}
          />
        </div>
      );
    };
    
    export default App;
    

    复制

    CodeSandbox上查看此示例。

    使用此组件,您可以通过使用该useEffect()Hook实现与以前相同的功能。您还优化了代码,因为您不必为componentDidMount()componentDidUpdate()函数编写单独的代码。使用useEffect()挂钩,您将获得两者的功能。这是因为useEffect()默认情况下,在初始渲染之后和每个后续更新之后都运行。

    第6步-转换PureComponentReact memo

    React PureComponent的工作方式类似于Component。它们之间的主要区别在于,React.Component它没有实现shouldComponentUpdate()生命周期方法,而React.PureComponent却没有实现。

    如果您的应用程序在render()给定相同的道具和状态的React.PureComponent情况下函数呈现的结果相同,则可以在某些情况下提高性能。

    React.memo()以类似的方式工作。当您的功能组件使用相同的道具呈现相同的结果时,可以将其包装在调用中React.memo()以提高性能。使用PureComponentReact.memo()给作出反应的应用性能的显着增加,因为它降低了在应用中呈现操作的数量。

    要了解它们的作用,您将首先查看组件每两秒钟渲染一次的代码,无论值或状态是否发生变化:

    ExampleClassComponent.js

    import React, { Component } from 'react';
    
    function Unstable(props) {
      // monitor how many times this component is rendered
      console.log('Rendered Unstable component');
      return (
        <div>
          <p>{props.value}</p>
        </div>
      );
    };
    
    class App extends Component {
      state = {
        value: 1
      };
    
      componentDidMount() {
        setInterval(() => {
          this.setState(() => {
            return { value: 1 };
          });
        }, 2000);
      }
    
      render() {
        return (
          <div>
            <Unstable value={this.state.value} />
          </div>
        );
      }
    }
    export default App;
    

    复制

    当您运行该应用程序并检查日志时,您会注意到它每两秒钟渲染一次组件,而状态或道具没有任何变化。使用PureComponent和可以改善这种情况React.memo()

    控制台日志输出,可进行多种渲染操作

    大多数时候,您只想在状态或道具发生变化时重新渲染组件。使用上面的示例,您可以对其进行改进,以PureComponent使组件仅在状态或道具发生变化时才重新渲染。

    您可以通过导入PureComponent和扩展它来完成此操作:

    ExamplePureComponent.js

    import React, { PureComponent } from 'react';
    
    function Unstable(props) {
      console.log('Rendered Unstable component');
      return (
        <div>
          <p>{props.value}</p>
        </div>
      );
    };
    
    class App extends PureComponent {
      state = {
        value: 1
      };
    
      componentDidMount() {
        setInterval(() => {
          this.setState(() => {
            return { value: 1 };
          });
        }, 2000);
      }
    
      render() {
        return (
          <div>
            <Unstable value={this.state.value} />
          </div>
        );
      }
    }
    
    export default App;
    

    复制

    现在,如果再次运行该应用程序,则只会获得初始渲染。在那之后没有其他事情发生。这是因为您有class App extends PureComponent {}而不是class App extends Component {}

    单个渲染操作的控制台日志输出

    这解决了不考虑当前状态而重新渲染组件的问题。但是,如果在setState方法中实现状态更改,则会遇到另一个问题。

    例如,考虑对的以下更改setState()

    当前value设置为1

    componentDidMount() {
      setInterval(() => {
        this.setState(() => {
          return { value: 1 };
        });
      }, 2000);
    }
    

    复制

    让我们考虑value设置为的情况Math.random()

    componentDidMount() {
      setInterval(() => {
        this.setState(() => {
          return { value: Math.round(Math.random()) };
        });
      }, 2000);
    }
    

    复制

    在这种情况下,第一个示例组件将在每次值更新为下一个随机数时重新呈现。但是,PureComponent仅当状态或道具发生更改时,才可以重新渲染组件。

    现在,您可以探索如何使用React.memo()以实现相同的修复程序。为此,将组件包装为React.memo()

    ExampleReactMemo.js

    import React, { Component } from 'react';
    
    const Unstable = React.memo(function Unstable (props) {
      console.log('Rendered Unstable component');
      return (
        <div>
          <p>{props.value}</p>
        </div>
      );
    });
    
    class App extends Component {
      state = {
        val: 1
      };
    
      componentDidMount() {
        setInterval(() => {
          this.setState(() => {
            return { value: 1 };
          });
        }, 2000);
      }
    
      render() {
        return (
          <div>
            <Unstable val={this.state.val} />
          </div>
        );
      }
    }
    
    export default App;
    

    复制

    这是此示例的CodeSandbox

    这样可获得与使用相同的结果PureComponent。该组件仅在初始渲染之后进行渲染,并且直到状态或道具发生更改后才重新进行渲染。

    结论

    在本教程中,您探索了几种使用React Hooks将现有的基于类的组件转换为功能组件的方法。

    您还查看了将ReactPureComponent类转换为的特殊情况React.memo()

    要在您的应用程序中使用Hooks,请确保将您的React版本更新为支持的版本:

    "react": "^16.7.0-alpha",
    "react-dom": "^16.7.0-alpha",
    

    复制

    现在,您有了一个基础,可以进一步使用React Hooks进行实验。

    https://www.digitalocean.com/community/tutorials/five-ways-to-convert-react-class-components-to-functional-components-with-react-hooks

  • 相关阅读:
    Ubuntu 20.04下EasyConnect兼容性问题临时解决方案
    Ubuntu 20.04 LTS安装搜狗输入法,只需三条命令,还能自动更新
    Java笔记: 继承成员覆盖和隐藏
    Java扫雷游戏: JMine
    Emacs: 设置窗口标题格式
    Java笔记: protected的真正含义
    Java笔记: 初始化块
    Ubuntu跨版本安装软件
    百度编辑器 Ueditor 增加字体
    AspCms 升级百度编辑器
  • 原文地址:https://www.cnblogs.com/c-x-a/p/14103808.html
Copyright © 2011-2022 走看看