关于hooks的理解与使用

871 阅读15分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

React Hooks

  • React Hooks 介绍
  • useState hook
  • useEffect hook

hooks是什么?

目标

能够说出react hooks是什么?

Hooks 是什么

Hooks:钩子、钓钩、钩住。 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

作用: 为函数组件提供状态、生命周期等原本 在Class 组件中才提供的功能

  • Hooks 只能在函数组件中使用
  • 可以理解为通过 Hooks 为函数组件钩入 class 组件的特性

Hooks 前后,组件开发模式的对比

  • React v16.8 以前: class 组件(提供状态) + 函数组件(展示内容)

  • React v16.8 及其以后:

    1. class 组件(提供状态) + 函数组件(展示内容)
    2. Hooks(提供状态) + 函数组件(展示内容)
    3. 混用以上两种方式:部分功能用 class 组件,部分功能用 Hooks+函数组件

小结

  1. 有了 Hooks 以后,不能再把函数组件称为 __ __ __了,因为 Hooks 可以为函数组件提供了状态。
  2. Hooks 是一些可以让你在 __ _组件里“钩入” __ _ 及 __ __ 等特性的 ****

为什么要有 Hooks

目标

能够说出为什么要有hooks,hooks能解决什么问题?

内容

两个角度:1 组件的状态逻辑复用 2 class 组件自身的问题

react组件的本质

React 是用于构建用户界面的 JavaScript 库 。

React组件是对特定功能的封装,主要用来对UI进行拆分。

React 组件的模型其实很直观,就是从 Model 到 View 的映射,这里的 Model 对应到 React 中就是 state 和 props

公式:组件(State+Props) = UI

class 组件自身的问题

根据状态来渲染UI这件事上,class 组件并没有发挥它最重要的功能:

  • 组件之间很少继承
  • 组件之间很少相互访问

函数式组件的好处

  1. 函数本身比较简单,更好的胜任根据状态来渲染UI这件事
  2. hooks让函数组件内部有了维护状态的能力
  3. hooks带来了组件的逻辑复用能力

小结

  1. 组件的本质工作是状态到UI的映射
  2. 相比类组件,hooks+函数组件能更好的胜任这个工作
  3. hooks带来了更强大的 __ __ 的能力

hooks的使用策略

目标

能够理解在react中什么场景应该使用hooks

策略

  1. react没有计划从React中移除class

  2. Hook 和现有代码可以同时工作,你可以渐进式地使用:

    1. 不推荐直接使用 Hooks 大规模重构现有组件
    2. 推荐新功能用 Hooks,复杂功能实现不了的,也可以继续用 class
    3. 找一个功能简单、非核心功能的组件开始使用 hooks
  3. class 组件相关的 API 在hooks中可以不用

    1. state与setState
    2. 钩子函数,componentDidMountcomponentDidUpdatecomponentWillUnmount
    3. `this 相关的用法
  4. 原来学习的内容还是要用的

    1. JSX:{}onClick={handleClick}、条件渲染、列表渲染、样式处理等
    2. 组件:函数组件、组件通讯
    3. React 开发理念:单向数据流状态提升

总结

  1. react官方 __ _React中移除class
  2. 可以在项目中同时使用 __ 和Class式组件

useState-基本使用-认识第一个hooks

学习的第一个hooks

目标

能够使用useState为函数组件提供状态

使用场景

当你想要在函数组件中,使用组件状态时,就要使用 useState 这个Hook 了,

作用

为函数组件提供状态(state)

步骤

  1. 导入。 useState 函数

  2. 调用 useState 函数,传入初始值,返回状态修改状态的函数

  3. 使用

    1. 在 JSX 中展示状态
    2. 特定的时机调用修改状态的函数来改状态

示例

import { useState } from 'react'
//  useState 是hook,hook是use开头的函数
const Count = () => {  
  // 0 是初始值
  // 返回值是一个数组
  const stateArray = useState(0)
​
  // 状态值 -> 0
  const state = stateArray[0]
  // 修改状态的函数
  const setState = stateArray[1]
​
  return (
    <div>
      {/* 展示状态值 */}
      <h1>useState Hook -> {state}</h1>
      {/* 点击按钮,让状态值 +1 */}
      <button onClick={() => setState(state + 1)}>+1</button>
    </div>
  )
}
  • 参数:状态初始值。比如,传入 0 表示该状态的初始值为 0

    • 注意:此处的状态可以是任意值(比如,数值、字符串等),而 class 组件中的 state 必须是对象
  • 返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState)

使用数组解构简化代码

使用数组解构简化 useState 的使用。约定:修改状态的函数名称以 set 开头,后面跟上状态的名称

// 解构出来的名称可以是任意名称
​
const [state, setState] = useState(0)
const [age, setAge] = useState(0)
const [count, setCount] = useState(0)

小结

  1. hooks是以是开头的 __

  2. useState的作用是 __ __ __ ___

  3. useState的格式是:

    1. 入参是?
    2. 返回值是?

useState-处理表单元素

目标

能够获取表单元素的值

思路

  1. 用useState初始化内容和修改内容的方法
  2. 向input元素上设置value和onChange属性

代码

import React, { useState } from 'react'
import ReactDom from 'react-dom'
export default function App () {
  const [content, setContent] = useState('')
  return (
    <div>
      {content}
      <input value={content} onChange={(e) => setContent(e.target.value)} />
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))
​

小结

useState可以轻松完成受控组件的功能

useState-setXXX回调函数格式

目标

掌握useState的参数是函数的格式

useState 两种格式

格式1:传入值

useState(0) useState('abc')

格式2:传入回调

useState(() => { return 初始值 })

  1. 回调函数的返回值就是状态的当前值
  2. 回调函数只会触发一次

使用场景

格式1:传入值

如果状态就是一个普通的数据(比如,字符串、数字、数组等)都可以直接使用 useState(普通的数据)

格式2:传入回调

  1. 初始状态需要经过一些计算得到。 useState(()=>{这里有一些计算, return 结果}))

setXXX的参数可以是回调

状态需要迭代累计。 setXXXXX((上一次的值) => { return 新值 })

示例-经典场景

希望state有连续叠加的效果

import React, { useState } from 'react'
import ReactDom from 'react-dom'
export default function App () {
  const [count, setCount] = useState(0)
  const hClick1 = () => {
    setCount(count+1)
    setCount(count+1)
    setCount(count+1)
  }
  const hClick2 = () => {
   
  }
  
  return (
    <div>
      count:{count}
      <button onClick={hClick1}>多次连续setCount-值</button>
      <button onClick={hClick2}>多次连续setCount-值</button>
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))
​

小结

  1. useState的参数有两种格式

    1. const [count, setCount] = useState(0)
    2. 回调函数 const [count, setCount] = useState(()=>{ return 初始值 })
  2. setXXX的参数有两个格式,其中函数式可以用来对state进行叠加

    setCount(100)
    ​
    setCount(initState => { 
      // ...
      return newState
    })
    
   
   
​
​
​
## 练习
​
点击按钮,开始从60倒计时
​
​
​
```js
import React, { useState } from 'react'import ReactDom from 'react-dom'export default function App () {
  const [count, setCount] = useState(10)
  const hClick = () => {
    setInterval(() => {
      setCount((count) => count - 1)
    }, 1000)
  }
  return (
    <div>
      <h1>{count}</h1>
​
      <button onClick={hClick}>倒数10个数</button>
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))
​

useState-组件的更新过程

目标

了解使用useState之后,组件的更新过程

背景

函数组件没有生命周期

更新过程

示例代码

import { useState } from 'react'const Count = () => {  
  console.log('Count...')
  const [count, setCount] = useState(0)
  return (
    <div>
      {/* 展示状态值 */}
      <h1>useState Hook -> {count}</h1>
      {/* 点击按钮,让状态值 +1 */}
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  )
}

更新过程

函数组件使用 useState hook 后的执行过程,以及状态值的变化:

  • 组件第一次渲染:

    1. 执行该函数中的代码逻辑
    2. 调用 useState(0) 将传入的参数作为状态初始值,即:0
    3. 渲染组件,此时,获取到的状态 count 值为: 0

用户点击按钮,调用 setCount(count + 1) 修改状态,因为状态发生改变,所以,该组件会重新渲染

  • 组件第二次渲染:

    1. 再次执行该组件中的代码逻辑

    2. 再次调用 useState(0)

      1. 此时 React 内部会拿到最新的状态值而非初始值
      2. 该案例中最新的状态值为 1
    3. 再次渲染组件,此时,获取到的状态 count 值为:1

useState 的初始值(参数)只会在组件第一次渲染时生效

const [count, setCount] = useState(()=>{
  console.log('useState...') // 这句只会输出一次
  return 0
})

也就是说,以后的每次渲染,useState 获取到都是最新的状态值。React 组件会记住每次最新的状态值!

小结

状态更新,整个组件的逻辑重新运行一次;

useState只会在组件第一次渲染时使用状态的初值,随后都会使用状态的最新值

useState 这个 Hook 就是用来管理 state 的,它可以让函数组件具有维持状态的能力。也就是说,在一个函数组件的多次渲染之间,这个 state 是共享的。

useState-最佳实践

目标

了解useState的最佳实践方式

如何为函数组件提供多个状态?

两种方案

  • 方案1:useState({状态1, 状态2.....})
  • 方案2: useState(状态1) useState(状态2)

推荐使用方案2

调用 useState Hook 多次即可,每调用一次 useState Hook 可以提供一个状态。

注意:

hooks: setXXX(新值) ==> 用新值去替换之前的值

class: setState({要修改的属性})

useState的使用规则

规则

  1. useState只能直接出现在 函数组件 内部

  2. useState不能嵌套在 if/for/

    原因: React 是按照 Hooks 的调用顺序来识别每一个 Hook,如果每次调用的顺序不同,导致 React 无法知道是哪一个 Hook。 可以通过开发者工具进行查看

useEffect-理解副作用

目标

能够说出什么是副作用

理解副作用

副作用(Side Effects;Adverse Reactions)系指应用治疗量的药物后所出现的治疗目的以外的药理作用

事物的主要作用之外的,就是副作用。

感冒药:

  • 主作用:用于感冒引起的头痛,发热,鼻塞,流涕,咽痛等
  • 副作用:可见困倦、嗜睡、口渴、虚弱感

函数式组件:

  • 主作用:就是根据数据(state/props)渲染 UI
  • 副作用:数据(Ajax)请求、手动修改 DOM、开启定时器,清空定时器,添加事件监听,删除事件, localStorage 操作等

总结

对于react组件来说,除了渲染UI之外的其他操作,都可以称之为副作用。

useEffect-基本使用

目标

能够在函数组件中使用useEffect,掌握它的使用格式和执行时机

使用步骤

// 1. 导入useEffect
import { useEffect } from 'react'// 2. 使用useEffect
useEffect(() => {
    console.log('useEffect 1 执行了,可以做副作用')
})
useEffect(() => {
    console.log('useEffect 2 执行了,可以做副作用')
})

执行时机

render工作完成之后,执行Effect;

如果定义了多个,则顺序执行;

案例

import React, { useState, useEffect } from 'react'
import ReactDom from 'react-dom'
export default function App () {
  useEffect(() => {
    console.log('useEffect')
    document.title = 'count' + count
  })
  const [count, setCount] = useState(0)
  return (
    <div
      onClick={() => {
        setCount(count + 1)
      }}>
      函数组件{count}
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))

小结

  1. 在实际开发中,副作用是不可避免的。因此,react 专门提供了 __ __ 来处理函数组件中的副作用
  2. 所有的副作用操作只能写在useEffect中吗?
  3. 步骤?
  4. 执行时机?

useEffect-设置依赖项

目标

能够设置useEffect的依赖,只在 count 变化时,才执行相应的 effect。

问题导入

import React, { useState, useEffect } from 'react'
import ReactDom from 'react-dom'
export default function App () {
  useEffect(() => {
    console.log('useEffect,更新title')
    document.title = 'count' + count
  })
  const [count, setCount] = useState(0)
  const [n, setN] = useState(1)
  return (
    <div>
      <h1>useEffect的依赖项</h1>
      <button
        onClick={() => {
          setCount(count + 1)
        }}>
        点击了{count}次,会更新到文档的标题上
      </button>
      <div>
        n:{n}
        <button onClick={() => setN(n + 1)}>点击,让n+1</button>
      </div>
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))
​

问题:

  1. 点击第2行中的代码,副作用会执行吗?
  2. 有必要执行么?

useEffect的依赖项

useEffect有两个参数:

参数1: 副作用函数。

参数2:执行副作用函数的依赖项:它决定了什么时机执行参数1(副作用函数)

useEffect的完整格式

情况1:不带第二个参数。执行时机:每次更新之后都要执行

情况2:带第二个参数,参数是空数组。执行时机:只执行第一次

useEffect(() => {
  // 副作用函数的内容
}, [])

使用场景:1 事件绑定 2 发送请求获取数据 等。

情况3:带第二个参数(数组格式),并指定了依赖项。执行时机:(1)初始执行一次 (2)依赖项的值变化了,执行一次

useEffect(() => {
  // 副作用函数的内容
}, [依赖项1,依赖项2,....])

这里的依赖项就是组件中定义的状态。

小结

  1. useEffect没有依赖项,副作用函数将如何执行?
  2. useEffect的依赖项是[], 副作用函数将如何执行?
  3. useEffect的依赖项是[count], 副作用函数将如何执行?

参考:

  1. useEffect完全指南:overreacted.io/zh-hans/a-c…
  2. 拓展阅读:dan abramov面试字节

useEffect典型应用场景-定时器

问题导入

import React, { useState } from 'react'
import ReactDom from 'react-dom'
export default function App () {
  const [count, setCount] = useState(0)
  setInterval(() => {
    setCount((count) => count + 1)
  })
  return (
    <div>
      {count}
      {/* <button onClick={hClick}>点击</button> */}
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))

购物车案例

目标

使用函数式组件+hooks来完成

组件划分

基本步骤

  1. 初始化项目基本结构
  2. 封装 MyHeader 组件
  3. 封装 MyFooter 组件
  4. 商品列表数据展示
  5. 封装 GoodsItem 组件
  6. 封装 MyCounter 组件

项目初始化

  • 清理目录
  • 安装bootstrap npm i bootstrap@4.5.0
  • 引入bootstrap样式文件
// App.js 
import 'bootstrap/dist/css/bootstrap.css'
import './app.scss'
​
​
return <div className="app"></div>

新增样式 App.scss

.app {
  padding-top: 45px;
  padding-bottom: 50px;
}

封装MyHeader 组件

目标

封装并使用Myheader组件

思路

  1. 创建组件,引入样式,导出组件
  2. 使用组件

新建组件

src/components/MyHeader/index.js

import './index.scss'
import React from 'react'export default function index() {
  return (
    <div className='my-header'>
      标题
    </div>
  )
}
​
  • 新建样式文件src/components/MyHeader/index.scss
.my-header {
  z-index: 999;
  height: 45px;
  line-height: 45px;
  text-align: center;
  background-color: #1d7bff;
  color: #fff;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
}
  • 注意:脚手架内置了scss的支持,但是需要安装scss依赖包
npm i sass

使用组件

App.js

import React from 'react'
import MyHeader from './components/MyHeader'
export default function App() {
  return (
    <div>
      <MyHeader>购物车</MyHeader>
    </div>
  )
}
​

封装MyFooter组件

目标

封装购物车的Footer组件

步骤

  1. 创建Footer组件
  2. 提供Footer样式
  3. 在App.js中渲染

核心代码:

  • 创建Footer组件 src/components/MyFooter.js
import React from 'react'
import './index.scss'
export default function MyFooter() {
  return (
  <div className="my-footer">
    <div className="custom-control custom-checkbox">
      <input type="checkbox" className="custom-control-input" id="footerCheck" />
      <label className="custom-control-label" htmlFor="footerCheck">全选</label>
    </div>
    <div>
      <span>合计:</span>
      <span className="price">¥ 100</span>
    </div>
    <button type="button" className="footer-btn btn btn-primary">结算 (0)</button>
  </div>
  )
}
​
  • 提供Footer样式src/components/MyFooter.scss
.my-footer {
  z-index: 999;
  position: fixed;
  bottom: 0;
  width: 100%;
  height: 50px;
  border-top: 1px solid #ccc;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 10px;
  background: #fff;
​
  .price {
    color: red;
    font-weight: bold;
    font-size: 15px;
  }
  .footer-btn {
    min-width: 80px;
    height: 30px;
    line-height: 30px;
    border-radius: 25px;
    padding: 0;
  }
}
  • App.js中渲染
import React from 'react'
import MyHeader from './components/MyHeader'
import MyFooter from './components/MyFooter'
export default function App() {
  return (
    <div>
      <MyHeader>购物车</MyHeader>
​
      <MyFooter></MyFooter>
    </div>
  )
}
​

封装GoodsItem组件

目标

封装GoodsItems组件

步骤

创建组件src/components/GoodsItem/index.js

import React from 'react'
import './index.scss'
export default function GoodsItem() {
  return (
    <div className="my-goods-item">
      <div className="left">
        <div className="custom-control custom-checkbox">
          <input type="checkbox" className="custom-control-input" id="input" />
          <label className="custom-control-label" htmlFor="input">
            <img
              src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
              alt=""
            />
          </label>
        </div>
      </div>
      <div className="right">
        <div className="top">商品名称</div>
        <div className="bottom">
          <span className="price">¥ 商品价格</span>
          <span>counter组件</span>
        </div>
      </div>
    </div>
  )
}
​
  • 准备样式

    建立src/components/GoodsItem/index.scss,内容如下:

.my-goods-item {
  display: flex;
  padding: 10px;
  border-bottom: 1px solid #ccc;
  .left {
    img {
      width: 120px;
      height: 120px;
      margin-right: 8px;
      border-radius: 10px;
    }
    .custom-control-label::before,
    .custom-control-label::after {
      top: 50px;
    }
  }
  .right {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    .bottom {
      display: flex;
      justify-content: space-between;
      padding: 5px 0;
      .price {
        color: red;
        font-weight: bold;
      }
    }
  }
}
​
  • 使用组件
import React from 'react'
import './App.scss'
import MyHeader from './components/MyHeader'
import MyFooter from './components/MyFooter'
import GoodsItem from './components/GoodsItem'
export default function App() {
  return (
    <div className="app">
      <MyHeader>购物车</MyHeader>
      <GoodsItem></GoodsItem>
      <GoodsItem></GoodsItem>
      <GoodsItem></GoodsItem>
      <GoodsItem></GoodsItem>
      <MyFooter></MyFooter>
    </div>
  )
}

GoodsItems-商品列表渲染

完成商品列表的数据渲染

步骤:

  1. app组件根据数据渲染商品列表
  2. GoodsItem接收数据进行渲染

核心代码

初始数据

const arr = [
  {
    id: 1,
    goods_name:
      '班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣',
    goods_img: 'https://www.escook.cn/vuebase/pics/1.png',
    goods_price: 108,
    goods_count: 1,
    goods_state: true,
  },
  {
    id: 2,
    goods_name:
      '嘉叶希连帽卫衣女春秋薄款2020新款宽松bf韩版字母印花中长款外套ins潮',
    goods_img: 'https://www.escook.cn/vuebase/pics/2.png',
    goods_price: 129,
    goods_count: 1,
    goods_state: true,
  },
  {
    id: 3,
    goods_name:
      '思蜜怡2020休闲运动套装女春秋季新款时尚大码宽松长袖卫衣两件套',
    goods_img: 'https://www.escook.cn/vuebase/pics/3.png',
    goods_price: 198,
    goods_count: 1,
    goods_state: false,
  },
  {
    id: 4,
    goods_name:
      '思蜜怡卫衣女加绒加厚2020秋冬装新款韩版宽松上衣连帽中长款外套',
    goods_img: 'https://www.escook.cn/vuebase/pics/4.png',
    goods_price: 99,
    goods_count: 1,
    goods_state: false,
  },
  {
    id: 5,
    goods_name:
      '幂凝早秋季卫衣女春秋装韩版宽松中长款假两件上衣薄款ins盐系外套潮',
    goods_img: 'https://www.escook.cn/vuebase/pics/5.png',
    goods_price: 156,
    goods_count: 1,
    goods_state: true,
  },
  {
    id: 6,
    goods_name: 'ME&CITY女装冬季新款针织抽绳休闲连帽卫衣女',
    goods_img: 'https://www.escook.cn/vuebase/pics/6.png',
    goods_price: 142.8,
    goods_count: 1,
    goods_state: true,
  },
  {
    id: 7,
    goods_name:
      '幂凝假两件女士卫衣秋冬女装2020年新款韩版宽松春秋季薄款ins潮外套',
    goods_img: 'https://www.escook.cn/vuebase/pics/7.png',
    goods_price: 219,
    goods_count: 2,
    goods_state: true,
  },
  {
    id: 8,
    goods_name:
      '依魅人2020休闲运动衣套装女秋季新款秋季韩版宽松卫衣 时尚两件套',
    goods_img: 'https://www.escook.cn/vuebase/pics/8.png',
    goods_price: 178,
    goods_count: 1,
    goods_state: true,
  },
  {
    id: 9,
    goods_name:
      '芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬',
    goods_img: 'https://www.escook.cn/vuebase/pics/9.png',
    goods_price: 128,
    goods_count: 1,
    goods_state: false,
  },
  {
    id: 10,
    goods_name:
      'Semir森马卫衣女冬装2019新款可爱甜美大撞色小清新连帽薄绒女士套头衫',
    goods_img: 'https://www.escook.cn/vuebase/pics/10.png',
    goods_price: 153,
    goods_count: 1,
    goods_state: false,
  },
]

用useState维护数据

const [list, setList] = useState(arr)

app.js渲染组件

{list.map((item) => (
  <GoodsItem key={item.id} {...item}></GoodsItem>
))}

子组件goodsItem渲染数据

import React from 'react'
import './index.scss'
export default function GoodsItem({
  goods_count,
  goods_img,
  goods_name,
  goods_price,
  goods_state,
  id,
}) {
  return (
    <div className="my-goods-item">
      <div className="left">
        <div className="custom-control custom-checkbox">
          <input
            type="checkbox"
            className="custom-control-input"
            checked={goods_state}
            id={id}
          />
          <label className="custom-control-label" htmlFor={id}>
            <img src={goods_img} alt="" />
          </label>
        </div>
      </div>
      <div className="right">
        <div className="top">{goods_name}</div>
        <div className="bottom">
          <span className="price">¥ {goods_price}</span>
          <span>counter组件</span>
        </div>
      </div>
    </div>
  )
}
​

GoodsItem-商品选中功能

目标

完成商品的选中切换功能

步骤

  1. 注册onChange事件
  2. 子传父修改状态

核心代码

子组件 goodsItem

调用父组件传入的函数

<input
  type="checkbox"
  className="custom-control-input"
  checked={goods_state}
  id={id}
  onChange={() => changeState(id)}
/>

父组件app.js

定义函数并传给子组件

const changeState = (id) => {
  setList(
    list.map((item) => {
      if (item.id === id) {
        return {
          ...item,
          goods_state: !item.goods_state,
        }
      } else {
        return item
      }
    })
  )
}
​
{list.map((item) => (
  <GoodsItem
    key={item.id}
    {...item}
    changeState={changeState}
  ></GoodsItem>
))}

MyFooter-商品全选功能

目标

完成商品全选切换功能

核心代码

  • 子组件

    定义isCheckAll。通过分析list中是否全部选中来决定

    给checkbox上绑定onChange

<input
  type="checkbox"
  className="custom-control-input"
  id="footerCheck"
  checked={isCheckAll}
  onChange={() => checkAll(!isCheckAll)}
/>
  • 父组件

定义函数checkAll

并传给子组件使用

const checkAll = (value) => {
  setList(
    list.map((item) => {
      return {
        ...item,
        goods_state: value,
      }
    })
  )
}
​
<MyFooter checkAll={checkAll} />

MyFooter-商品数量与价格的显示

步骤:

  1. 父组件把list传递给子组件
<MyFooter list={list}></MyFooter>
  1. 子组件计算总数量和总价钱
const totalCount = list
  .filter((item) => item.goods_state)
  .reduce((prev, item) => prev + item.goods_count, 0)
const totalPrice = list
  .filter((item) => item.goods_state)
  .reduce((prev, item) => prev + item.goods_price * item.goods_count, 0)

useEffect数据持久化

用useEffect做数据持久化

// 获取
const [list, setList] = useState(() => {
  return JSON.parse(localStorage.getItem('list')) || arr
})
​
​
useEffect(() => {  
  localStorage.setItem('list', JSON.stringify(list))
}, [list])
``