• 常见React Hooks 钩子函数用法


    一、useState

    useState()用于为函数组件引入状态(state)。纯函数不能有状态,所以把状态放在钩子里面。

    1. import React, { useState } from 'react'
    2. import './Button.css'
    3. export function UseStateWithoutFunc() {
    4. const [name, setName] = useState('何遇')
    5. const [age, setAge] = useState()
    6. function onChange() {
    7. setName('张三') // 修改name
    8. setAge(23) // 修改age
    9. }
    10. return (
    11. <>
    12. <div className={'nameButton'}>姓名: {name}</div>
    13. <div className={'ageButton'}>年龄: {age === undefined ? '未知' : age}</div>
    14. <button onClick={onChange}>click</button>
    15. </>
    16. )
    17. }

    上面代码中,UseStateWithoutFunc 组件是一个函数,内部使用useState()钩子引入状态。

    useState()这个函数接受状态的初始值,作为参数,上例的初始值为显示的姓名文字。该函数返回一个数组,数组的第一个成员是一个变量(上例是name),指向状态的当前值。第二个成员是一个函数,用来更新状态,约定是set前缀加上状态的变量名(上例是setName)。

    当没有赋予初始值的时候,这个变量则是undefined,没有定义的。当再次执行set函数的时候,则会被赋值。如上述年龄字段age,一开始是undefined的,所以显示出来的效果就是“未知”。

    当点击button的时候,触发onChange函数,同时给年龄和姓名赋新值。因此点击按钮之后显示效果如下:

    二、useEffect

    seEffect()用来引入具有副作用的操作,最常见的就是向服务器请求数据。以前,放在componentDidMount里面的代码,现在可以放在useEffect()。

    useEffect()的用法如下:

    1. useEffect(() => {
    2. // Async Action
    3. }, [dependencies]);

    上面用法中,useEffect()接受两个参数。

    • 第一个参数是一个函数,异步操作的代码放在里面。
    • 第二个参数是一个数组,用于给出 Effect 的依赖项,只要这个数组发生变化,useEffect()就会执行。第二个参数可以省略,这时每次组件渲染时,就会执行useEffect()。

    下面是Person.js和Person.css代码

    1. import {useEffect, useState} from "react";
    2. import "./Person.css";
    3. const Person = ({personId}) => {
    4. const[loading, setLoading] = useState(true);
    5. const[person, setPerson] = useState({});
    6. useEffect(() => {
    7. setLoading(true);
    8. fetch(`https://swapi.dev/api/people/${personId}`)
    9. .then(response => response.json())
    10. .then(data => {
    11. setLoading(false)
    12. setPerson(data)
    13. });
    14. }, [personId]);
    15. if (loading) {
    16. return <p>Loading...p>
    17. }
    18. return <div>
    19. <p>You're viewing: {person.name}p>
    20. <p>Height: {person.height}p>
    21. <p>Mass: {person.mass}p>
    22. div>
    23. }
    24. export default function PersonComponent() {
    25. const [show, setShow] = useState("1");
    26. return (
    27. <div className="App">
    28. <Person personId={show}>Person>
    29. <div>
    30. Show:
    31. <button onClick={() => setShow("3")}>R2-D2button>
    32. <button onClick={() => setShow("2")}>C-3PObutton>
    33. div>
    34. div>
    35. )
    36. }
    1. * {
    2. margin: 0;
    3. padding: 0;
    4. }
    5. body {
    6. padding: 10px;
    7. background-color: #f5f5fa;
    8. color: #222;
    9. }
    10. h2 {
    11. margin-bottom: 20px;
    12. font-family: 'Helvetica';
    13. font-weight: 400;
    14. }
    15. .App {
    16. font-family: 'Helvetica';
    17. font-weight: 200;
    18. text-align: left;
    19. width: 400px;
    20. }
    21. input {
    22. border: 1px solid #ddd;
    23. background-color: #fff;
    24. padding: 5px 10px;
    25. margin: 10px 0;
    26. border-radius: 5px;
    27. width: 300px;
    28. }
    29. button {
    30. padding: 5px 20px;
    31. background: #0066cc;
    32. border: 1px solid #fff;
    33. border-radius: 15px;
    34. color: #fff;
    35. margin-top: 10px;
    36. margin-bottom: 10px;
    37. }
    38. button:hover {
    39. cursor: pointer;
    40. box-shadow: 0px 2px 4px #0044aa40;
    41. }
    42. button:active {
    43. background: #0044aa;
    44. }
    45. button:focus {outline:0;}
    46. ul {
    47. margin-left: 20px;
    48. }
    49. .navbar {
    50. display: flex;
    51. flex-direction: row;
    52. justify-content: space-between;
    53. padding-bottom: 10px;
    54. margin-bottom: 20px;
    55. border-bottom: 1px solid #ccc;
    56. }
    57. .messages h1 {
    58. margin-bottom: 20px;
    59. }
    60. .messages p {
    61. margin-bottom: 10px;
    62. }
    63. .message {
    64. background-color: #fff;
    65. border: 1px solid #ddd;
    66. padding: 10px;
    67. border-radius: 4px;
    68. }

    在请求https://swapi.dev/api/people/{personId}/ 这个接口,每次传入不同的personId值,就可以请求到不同人的数据。

    每次personId发生改变之后,Person组件里的useEffect里的方法就会执行一次也就是会去请求一次后端数据,请求到了之后再刷新界面。没有请求到的时候由于loading默认设为true,因此就会执行return

    Loading...

    这段代码,就会展示Loading的文字。等请求到之后,会执行这行 setLoading(false),将loading字段设为false,因此此时就不会展示Loading的文字,而是展示姓名、身高等字样。

    点击R2-D2数据还没请求到的时候,会显示Loading

    请求到对应的数据之后,就会展示对应的个人信息

    三、useReducer

    React 本身不提供状态管理功能,通常需要使用外部库。这方面最常用的库是 Redux。

    Redux 的核心概念是,组件发出 action 与状态管理器通信。状态管理器收到 action 以后,使用 Reducer 函数算出新的状态,Reducer 函数的形式是(state, action) => newState。

    useReducers()钩子用来引入 Reducer 功能。

    const [state, dispatch] = useReducer(reducer, initialState);

    上面是useReducer()的基本用法,它接受 Reducer 函数和状态的初始值作为参数,返回一个数组。数组的第一个成员是状态的当前值,第二个成员是发送 action 的dispatch函数。

    下面是一个计数器的例子。用于计算状态的 Reducer 函数如下。

    1. const myReducer = (state, action) => {
    2. switch(action.type) {
    3. case('countUp'):
    4. return {
    5. ...state,
    6. count: state.count + 1
    7. }
    8. default:
    9. return state;
    10. }
    11. }

    组件代码如下。

    1. export default function UseReducerComponent() {
    2. const[state, dispatch] = useReducer(myReducer, {count: 0})
    3. return (
    4. <div className="App">
    5. <button onClick={() => dispatch({type: 'countUp'})}>
    6. +1
    7. </button>
    8. <p>Count: {state.count}</p>
    9. </div>
    10. )
    11. }

    1. import {useReducer} from "react";
    2. import "./styles.css";
    3. const myReducer = (state, action) => {
    4. switch (action.type) {
    5. case ('countUp'):
    6. return {
    7. ...state,
    8. count: state.count + 1
    9. }
    10. default:
    11. return state
    12. }
    13. }
    14. export default function UseReducerComponent() {
    15. const[state, dispatch] = useReducer(myReducer, {count: 0})
    16. return (
    17. <div className="App">
    18. <button onClick={() => dispatch({type: 'countUp'})}>
    19. +1
    20. </button>
    21. <p>Count: {state.count}</p>
    22. </div>
    23. )
    24. }

    styles.css样式如下:

    1. * {
    2. margin: 0;
    3. padding: 0;
    4. }
    5. body {
    6. padding: 10px;
    7. background-color: #f5f5fa;
    8. color: #222;
    9. }
    10. h2 {
    11. margin-bottom: 20px;
    12. font-family: 'Helvetica';
    13. font-weight: 400;
    14. }
    15. .App {
    16. font-family: 'Helvetica';
    17. font-weight: 200;
    18. text-align: left;
    19. width: 400px;
    20. }
    21. input {
    22. border: 1px solid #ddd;
    23. background-color: #fff;
    24. padding: 5px 10px;
    25. margin: 10px 0;
    26. border-radius: 5px;
    27. width: 300px;
    28. }
    29. button {
    30. padding: 5px 20px;
    31. background: #0066cc;
    32. border: 1px solid #fff;
    33. border-radius: 15px;
    34. color: #fff;
    35. margin-top: 10px;
    36. margin-bottom: 10px;
    37. }
    38. button:hover {
    39. cursor: pointer;
    40. box-shadow: 0px 2px 4px #0044aa40;
    41. }
    42. button:active {
    43. background: #0044aa;
    44. }
    45. button:focus {outline:0;}
    46. ul {
    47. margin-left: 20px;
    48. }
    49. .navbar {
    50. display: flex;
    51. flex-direction: row;
    52. justify-content: space-between;
    53. padding-bottom: 10px;
    54. margin-bottom: 20px;
    55. border-bottom: 1px solid #ccc;
    56. }
    57. .messages h1 {
    58. margin-bottom: 20px;
    59. }
    60. .messages p {
    61. margin-bottom: 10px;
    62. }
    63. .message {
    64. background-color: #fff;
    65. border: 1px solid #ddd;
    66. padding: 10px;
    67. border-radius: 4px;
    68. }

    四、useContext

    如果需要在组件之间共享状态,可以使用useContext()。

    现在有两个组件 Navbar 和 Messages,我们希望它们之间共享状态。

    1. <div className="App">
    2. <Navbar/>
    3. <Messages/>
    4. </div>

    第一步就是使用 React Context API,在组件外部建立一个 Context。

    const AppContext = React.createContext({});

    组件封装代码如下。

    1. <AppContext.Provider value={{
    2. username: 'superawesome'
    3. }}>
    4. <div className="App">
    5. <Navbar/>
    6. <Messages/>
    7. </div>
    8. </AppContext.Provider>

    上面代码中,AppContext.Provider提供了一个 Context 对象,这个对象可以被子组件共享。

    Navbar 组件的代码如下。下面代码中,useContext()钩子函数用来引入 Context 对象,从中获取username属性

    1. const Navbar = () => {
    2. const { username } = useContext(AppContext);
    3. return (
    4. <div className="navbar">
    5. <p>AwesomeSite</p>
    6. <p>{username}</p>
    7. </div>
    8. );
    9. }

    Message 组件的代码如下。下面代码中,useContext()钩子函数用来引入 Context 对象,从中获取username属性

    1. const Messages = () => {
    2. const { username } = useContext(AppContext)
    3. return (
    4. <div className="messages">
    5. <h1>Messages</h1>
    6. <p>1 message for {username}</p>
    7. <p className="message">useContext is awesome!</p>
    8. </div>
    9. )
    10. }

    UseContextComponent.js代码如下所示:

    1. import React, { useContext } from "react";
    2. import "./style.css";
    3. const AppContext = React.createContext({});
    4. const Navbar = () => {
    5. const { username } = useContext(AppContext)
    6. return (
    7. <div className="navbar">
    8. <p>AwesomeSite</p>
    9. <p>{username}</p>
    10. </div>
    11. )
    12. }
    13. const Messages = () => {
    14. const { username } = useContext(AppContext)
    15. return (
    16. <div className="messages">
    17. <h1>Messages</h1>
    18. <p>1 message for {username}</p>
    19. <p className="message">useContext is awesome!</p>
    20. </div>
    21. )
    22. }
    23. export default function UseContextComponent() {
    24. return (
    25. <AppContext.Provider value={{
    26. username: 'superawesome'
    27. }}>
    28. <div className="App">
    29. <Navbar />
    30. <Messages />
    31. </div>
    32. </AppContext.Provider>
    33. );
    34. }

    style.css文件如下:

    1. * {
    2. margin: 0;
    3. padding: 0;
    4. }
    5. body {
    6. padding: 10px;
    7. background-color: #f5f5fa;
    8. color: #222;
    9. }
    10. h2 {
    11. margin-bottom: 20px;
    12. font-family: 'Helvetica';
    13. font-weight: 400;
    14. }
    15. .App {
    16. font-family: 'Helvetica';
    17. font-weight: 200;
    18. text-align: left;
    19. width: 400px;
    20. }
    21. input {
    22. border: 1px solid #ddd;
    23. background-color: #fff;
    24. padding: 5px 10px;
    25. margin: 10px 0;
    26. border-radius: 5px;
    27. width: 300px;
    28. }
    29. button {
    30. padding: 5px 20px;
    31. background: #0066cc;
    32. border: 1px solid #fff;
    33. border-radius: 15px;
    34. color: #fff;
    35. margin-top: 10px;
    36. margin-bottom: 10px;
    37. }
    38. button:hover {
    39. cursor: pointer;
    40. box-shadow: 0px 2px 4px #0044aa40;
    41. }
    42. button:active {
    43. background: #0044aa;
    44. }
    45. button:focus {outline:0;}
    46. ul {
    47. margin-left: 20px;
    48. }
    49. .navbar {
    50. display: flex;
    51. flex-direction: row;
    52. justify-content: space-between;
    53. padding-bottom: 10px;
    54. margin-bottom: 20px;
    55. border-bottom: 1px solid #ccc;
    56. }
    57. .messages h1 {
    58. margin-bottom: 20px;
    59. }
    60. .messages p {
    61. margin-bottom: 10px;
    62. }
    63. .message {
    64. background-color: #fff;
    65. border: 1px solid #ddd;
    66. padding: 10px;
    67. border-radius: 4px;
    68. }

    可以理解为一种共享上下文状态。类似于Android里context的一样,获取上下文信息的。

    第一块是组件Navbar展示的

    第二块是组件Message展示的

    引用文献:

    【1】轻松学会 React 钩子:以 useEffect() 为例 - 阮一峰的网络日志

    【2】React Hooks 入门教程 - 阮一峰的网络日志

  • 相关阅读:
    【VSCode 插件商城无法搜索到插件的解决方法】
    四大函数式接口(重点,必须掌握)
    Docker
    通过开源如何赚钱生存发展
    加密算法 — — 对token进行非对称加密【RSA】
    浅谈数据和人工智能项目的管理
    Android设置应用图标
    【JavaEE】初识计算机网络(TCP/IP五层模型及封装和分用)
    java高校防疫物资管理系统计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
    从20s优化到500ms,我用了这三招
  • 原文地址:https://blog.csdn.net/qq_41688840/article/details/134271030