前言
eg:代表代码对照
若文章有误,欢迎读者留言反馈
React Hooks
函数式编程变成越来越流行,函数组件通过hooks也能像类组件保存数据状态,使用类似生命周期函数,
Hooks【钩子,把功能拿过来用】简介:
在React
的世界中,有容器组件和UI
组件之分,在React Hooks
出现之前,UI
组件我们可以使用函数,无状态组件来展示UI
,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给UI
组件进行渲染。在我看来,使用React Hooks
相比于从前的类组件有以下几点好处:
- 代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的生命周期函数中,容易使开发者不利于维护和迭代,通过
React Hooks
可以将功能代码聚合,方便阅读维护
- 组件树层级变浅,在原本的代码中,我们经常使用
HOC/render props
等方式来复用组件的状态,增强功能等,无疑增加了组件树层数及渲染,而在React Hooks
中,这些功能都可以通过强大的自定义的Hooks
来实现
useState保存数据状态【实习了类似state】
函数组件不能放数据状态,没有生命周期,没有实例,没有this,使用useState这个钩子函数
对比之前类组件保存组件状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import React, { Component } from 'react'
class ClassComponent extends Component { state = { arr: [1,2,3,4] } render() { return ( <div> <h2>类组件与函数组件使用hooks对比</h2> <p>类组件遍历</p> <ul> { this.state.arr.map((item, index) => { return <li key={index}>{item}</li> }) } </ul> </div> ) } }
export default ClassComponent
|
通过传入 useState 参数后返回一个带有默认状态和改变状态函数的数组。通过传入新状态给函数来改变原本的状态值。**值得注意的是 useState 不帮助你处理状态,相较于 setState 非覆盖式更新状态,useState 覆盖式更新状态,需要开发者自己处理逻辑。
函数组件使用useState
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import React, { useState } from 'react'
const ClassComponent = () => { const [arr] = useState([1, 2, 3, 4]) return ( <div> <h2>类组件与函数组件使用hooks对比</h2> <p>函数组件遍历</p> <ul> { arr.map((item, index) => { return <li key={index}>{item}</li> }) } </ul> </div> ) }
export default ClassComponent
|
useState工作中常用语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import React, { useState } from 'react'
const UseStateDemo = () => { const [num, setNum] = useState(0) const [bool, setBool] = useState(false) const [str, setStr] = useState('hello') const [arr, setArr] = useState([1,2,3,4]) const [obj, setObj] = useState({name: '小明', age: 18}) return ( <div> <h3>useState基本使用</h3> {/* 数字类型 */} <p>num值:{num}</p> {/* 传参和类组件一样也需要回调 */} <button onClick={() => setNum(num + 1)}>点击num加1</button> {/* 布尔值 */} <p>bool值:{JSON.stringify(bool)}</p> <button onClick={() => setBool(!bool)}>点击取反</button> {/* 字符串类型 */} <p>str值:{str}</p> <button onClick={() => setStr(str+'world')}>点击拼接world</button> {/* 数组 */} <p>arr值:{arr}</p> <button onClick={() => setArr([...arr, 5])}>点击往arr里添加元素</button> {/* 对象 */} <p>对象值:{JSON.stringify(obj)}</p> {/* 使用...对象覆盖,遵循不可变值, */} <button onClick={() => setObj({...obj, name: '小红'})}>点击修改对象里的name</button> </div> ) }
export default UseStateDemo
|
似乎有了useState
后,函数组件也可以拥有自己的状态了,但仅仅是这样完全不够。
useEffect处理副作用【实现了类似监听和生命周期】
useEffect
处理副作用,在组件渲染节点的同时可以做一些我们自己的逻辑,我们就叫副作用。
- useEffect 用法1
说明:类似于watch监听,但是useEffect首次会执行一次里面回调函数,而watch则不会,可以设置immediate:true就能首次监听了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import React, { useState, useEffect } from 'react'
const UseEffectDemo = () => { const [num, setNum] = useState(0) const [str, setStr] = useState('aaa') useEffect(() => { console.log('我被调用了') }, [num, str]) return ( <div> <h3>useEffect处理副作用</h3> <p>{num}</p> <button onClick={() => setNum(num + 1)}>点击+1</button> </div> ) }
export default UseEffectDemo
|
- useEffect 用法2
说明:参数2是空数组情况下,可以当做生命周期componentDidMount 挂载后来使用,挂载后会自执行一次,由于没有依赖项,也就不会再执行里面回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import React, { useState, useEffect } from 'react'
const UseEffectDemo = () => { const [num, setNum] = useState(0) const [str, setStr] = useState('aaa') useEffect(() => { console.log('我被调用了') }, []) return ( <div> <h3>useEffect处理副作用</h3> <p>{num}</p> <button onClick={() => setNum(num + 1)}>点击+1</button> </div> ) }
export default UseEffectDemo
|
- useEffect 用法3
说明:空着不写参数2的情况下,组件更新就会调用一次,当做componentDidUpdate更新后生命周期来使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import React, { useState, useEffect } from 'react'
const UseEffectDemo = () => { const [num, setNum] = useState(0) const [str, setStr] = useState('aaa') useEffect(() => { console.log('被调用了') }) return ( <div> <h3>useEffect处理副作用</h3> <p>{num}</p> <button onClick={() => setNum(num + 1)}>点击+1</button> </div> ) }
export default UseEffectDemo
|
- useEffect 用法4
说明:参数1 回调中的return后面的函数中可以做清除计时器,dom事件,自定义事件等清理工作
1 2 3 4 5 6 7 8
| useEffect(() => { return () => { } }, [num])
|
useContext【实现了类似跨层级通信】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import React, { createContext, useState, useContext } from 'react'
var context = createContext()
const ContextDemo = () => { const [arr, setArr] = useState([1,2,3,4]) return ( <div> <h3>父组件</h3> // 父组件向后提供数据需要使用context.Provider,设置value属性往后传递数据即可 <context.Provider value={arr}> <Child1 /> </context.Provider> </div> ) }
export default ContextDemo
function Child1() { return ( <div> <h3>儿子组件</h3> <Child2 /> </div> ) }
function Child2() { var arr = useContext(context) return ( <div> <h3>孙子组件</h3> <p>{arr}</p> </div> ) }
|
useReducer【实现了类似Redux/React-Redux】
useReducer
这个Hooks
在使用上几乎跟Redux/React-Redux
一模一样,唯一缺少的就是无法使用redux
提供的中间件,redux
可以通过中间件来增加的,redux-thunk
中间件可以书写异步。useReducer
是redux
的简化版,不支持异步
用法跟Redux
基本上是一致的,用法也很简单,算是提供一个mini
的Redux
版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import React, { useReducer } from 'react'
const initState = { num: 0 }
const reducer = (state, actions) => { if (actions.type === 'add') { return { num: state.num + actions.val } } else if (actions.type === 'sub') { return { num: state.num - actions.val } } else { return new Error('没有这个操作') } }
const UseReducerDemo = () => { const [state, dispatch] = useReducer(reducer, initState) return ( <div> <h3>简化版redux用法</h3> <p>{state.num}</p> { /* dispatch派送一个对象,里面有规则和传递数据 */ } <button onClick={() => dispatch({type: 'add', val: 1})}>点击+1</button> <button onClick={() => dispatch({type: 'sub', val: 1})}>点击-1</button> </div> ) }
export default UseReducerDemo
|