如何使用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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章