Hooks

Hook 是React 16.8的新特性,他可以让你在 function 组件中使用 state 以及其他的 React 特性。
Hooks的特点:

  • 使你在无需修改组件结构的情况下复用状态逻辑
  • 可将组件中相互关联的部分拆分成更小的函数,复杂组件将变得更容易理解
  • 更简洁、更易理解代码

Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:

  • 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用
  • 只能在 React 的函数组件中调用 Hook

    useState

    useState(initialState) 接收初始状态,返回一个由状态和更新函数组成的数组

  1. import React, { useState } from 'react';
  2. // 声明列表组件
  3. function FruitList ({ fruits, onSetFruit }) {
  4. return (
  5. <ul>
  6. { fruits.map(item => <li onClick={ () => onSetFruit(item)} key={item}>{item}</li>) }
  7. </ul>
  8. )
  9. }
  10. // 声明输入组件
  11. function FruitAdd (props) {
  12. const [pname, setPname] = useState("") // 键盘事件处理
  13. const onAddFruit = e => {
  14. if (e.key === "Enter") {
  15. props.onAddFruit(pname);
  16. setPname("");
  17. }
  18. }
  19. return (
  20. <div>
  21. <input
  22. placeholder="请输入需要添加的水果"
  23. type="text"
  24. value={pname}
  25. onChange={e => setPname(e.target.value)}
  26. onKeyDown={onAddFruit}
  27. />
  28. </div>
  29. )
  30. }
  31. export default function HooksTest() {
  32. // useState(initialState) 接收初始状态,返回一个由状态和更新函数组成的数组
  33. const [fruit, setFruit] = useState("")
  34. const [fruits, setFruits] = useState(['香蕉', '草莓', '芒果'])
  35. return (
  36. <div>
  37. <p>{ fruit === "" ? "请选择喜爱的水果" : `你的的选择是:${fruit}` }</p>
  38. <FruitList fruits={fruits} onSetFruit={setFruit}></FruitList>
  39. <FruitAdd onAddFruit={pname => setFruits([...fruits, pname])}></FruitAdd>
  40. </div>
  41. );
  42. }

useEffect

useEffect 给函数组件增加了执行副作用操作的能力。
副作用(Side Effect)是指一个 function 做了和本身运算、返回值无关的事,比如:修改了全局变量、修改了传入的 参数、甚至是 console.log(),所以 ajax 操作,修改 dom 都是算作副作用。

  • 异步获取数据 ```javascript import { useEffect } from “react”;

useEffect(()=>{ setTimeout(() => { setFruits([‘香蕉’,’西瓜’]) }, 1000); },[]) // 设置空数组意为没有依赖,则副作用操作仅执行一次

  1. 如果副作用对某状态有依赖,需要添加依赖选项
  2. ```javascript
  3. useEffect(() => {
  4. document.title = fruit;
  5. }, [fruit]);
  • 清除工作:有一些副作用是需要清除的,清除工作非常重要,可以防止内存泄漏

    1. useEffect(() => {
    2. const timer = setInterval(() => {
    3. console.log('msg')
    4. }, 1000)
    5. // 返回一个清理函数,组件卸载后会执行清理函数
    6. return function () {
    7. clearInterval(timer)
    8. }
    9. })

    useReducer

    useReducer是useState的可选项,常用于组件有复杂状态逻辑时,类似于redux中reducer概念。

范例:使用 useReducer 改写上面的代码

  1. import React, { useState, useEffect, useReducer } from 'react';
  2. function FruitList ({ fruits, onSetFruit }) {
  3. return (
  4. <ul>
  5. { fruits.map(item => <li onClick={ () => onSetFruit(item)} key={item}>{item}</li>) }
  6. </ul>
  7. )
  8. }
  9. // 声明输入组件
  10. function FruitAdd (props) {
  11. const [pname, setPname] = useState("") // 键盘事件处理
  12. const onAddFruit = e => {
  13. if (e.key === "Enter") {
  14. props.onAddFruit(pname);
  15. setPname("");
  16. }
  17. }
  18. return (
  19. <div>
  20. <input
  21. placeholder="请输入想要添加的水果"
  22. type="text"
  23. value={pname}
  24. onChange={e => setPname(e.target.value)}
  25. onKeyDown={onAddFruit}
  26. />
  27. </div>
  28. )
  29. }
  30. // 添加fruit状态维护fruitReducer
  31. function fruitReducer (state, action) {
  32. switch(action.type) {
  33. case "init":
  34. return action.payload
  35. case "add":
  36. return [...state, action.payload]
  37. default:
  38. return state
  39. }
  40. }
  41. export default function HooksTest() {
  42. // useState(initialState) 接收初始状态,返回一个由状态和更新函数组成的数组
  43. const [fruit, setFruit] = useState("")
  44. // 接收一个 reducer 和初始值,返回 state 和 dispatch
  45. const [fruits, dispatch] = useReducer(fruitReducer, [])
  46. // useEffect 接收回调函数和一个依赖数组(依赖为空时,useEffect函数只执行一次)
  47. useEffect(() => {
  48. setTimeout(() => {
  49. // setFruits(['香蕉', '草莓', '芒果'])
  50. dispatch({ type: 'init', payload: ['香蕉', '草莓', '芒果']})
  51. }, 1000)
  52. }, [])
  53. // 如果副作用操作对某状态有依赖,需要添加依赖选项
  54. useEffect(() => {
  55. document.title = fruit; },
  56. [fruit]);
  57. // 清除工作:有一些副作用是需要清除的,清除工作非常重要,可以防止内存泄漏
  58. useEffect(() => {
  59. const timer = setInterval(() => {
  60. console.log('msg')
  61. }, 1000)
  62. // 返回一个清理函数,组件卸载后会执行清理函数
  63. return function () {
  64. clearInterval(timer)
  65. }
  66. })
  67. return (
  68. <div>
  69. <p>{ fruit === "" ? "请选择喜爱的水果" : `你的的选择是:${fruit}` }</p>
  70. <FruitList fruits={fruits} onSetFruit={setFruit}></FruitList>
  71. <FruitAdd onAddFruit={pname => dispatch({ type: 'add', payload: pname })}></FruitAdd>
  72. </div>
  73. );
  74. }

useContext

useContext用于在快速在函数组件中导入上下文,useContext可以实现组件之间的解耦,复杂组件会更变得更加容易理解。

  1. import React, { useState, useEffect, useReducer, useContext } from 'react';
  2. function FruitList ({ fruits, onSetFruit }) {
  3. return (
  4. <ul>
  5. { fruits.map(item => <li onClick={ () => onSetFruit(item)} key={item}>{item}</li>) }
  6. </ul>
  7. )
  8. }
  9. // 声明输入组件
  10. function FruitAdd (props) {
  11. // 使用 useContent 获取上下文
  12. const { dispatch } = useContext(Context)
  13. const [pname, setPname] = useState("") // 键盘事件处理
  14. const onAddFruit = e => {
  15. if (e.key === "Enter") {
  16. // props.onAddFruit(pname);
  17. dispatch({ type: 'add', payload: pname })
  18. setPname("");
  19. }
  20. }
  21. return (
  22. <div>
  23. <input
  24. placeholder="请输入想要添加的水果"
  25. type="text"
  26. value={pname}
  27. onChange={e => setPname(e.target.value)}
  28. onKeyDown={onAddFruit}
  29. />
  30. </div>
  31. )
  32. }
  33. // 添加fruit状态维护fruitReducer
  34. function fruitReducer (state, action) {
  35. switch(action.type) {
  36. case "init":
  37. return action.payload
  38. case "add":
  39. return [...state, action.payload]
  40. default:
  41. return state
  42. }
  43. }
  44. // 创建上下文对象
  45. const Context = React.createContext()
  46. export default function HooksTest() {
  47. // useState(initialState) 接收初始状态,返回一个由状态和更新函数组成的数组
  48. const [fruit, setFruit] = useState("")
  49. // const [fruits, setFruits] = useState([])
  50. // 接收一个 reducer 和初始值,返回 state 和 dispatch
  51. const [fruits, dispatch] = useReducer(fruitReducer, [])
  52. // useEffect 接收回调函数和一个依赖数组(依赖为空时,useEffect函数只执行一次)
  53. useEffect(() => {
  54. setTimeout(() => {
  55. // setFruits(['香蕉', '草莓', '芒果'])
  56. dispatch({ type: 'init', payload: ['香蕉', '草莓', '芒果']})
  57. }, 1000)
  58. }, [])
  59. // 如果副作用操作对某状态有依赖,需要添加依赖选项
  60. useEffect(() => {
  61. document.title = fruit; },
  62. [fruit]);
  63. // 清除工作:有一些副作用是需要清除的,清除工作非常重要,可以防止内存泄漏
  64. useEffect(() => {
  65. const timer = setInterval(() => {
  66. console.log('msg')
  67. }, 1000)
  68. // 返回一个清理函数,组件卸载后会执行清理函数
  69. return function () {
  70. clearInterval(timer)
  71. }
  72. })
  73. return (
  74. <Context.Provider value={{ fruits, dispatch }}>
  75. <div>
  76. <p>{ fruit === "" ? "请选择喜爱的水果" : `你的的选择是:${fruit}` }</p>
  77. <FruitList fruits={fruits} onSetFruit={setFruit}></FruitList>
  78. <FruitAdd></FruitAdd>
  79. </div>
  80. </Context.Provider>
  81. );
  82. }