react-HOOK---一篇搞定react-hook,既生react,何生hook,深挖hook中常用的四个API

    技术2022-07-10  119

    为什么会有Hooks?

    介绍Hooks之前,首先要给大家说一下React的组件创建方式,一种是类组件,一种是纯函数组件,并且React团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。也就是说组件的最佳写法应该是函数,而不是类。。

    但是我们知道,在以往开发中类组件和纯函数组件的区别是很大的,纯函数组件有着类组件不具备的多种特点,简单列举几条

    纯函数组件没有状态纯函数组件没有生命周期纯函数组件没有this只能是纯函数

    这就注定,我们所推崇的函数组件,只能做UI展示的功能,涉及到状态的管理与切换,我们不得不用类组件或者redux,但我们知道类组件的也是有缺点的,比如,遇到简单的页面,你的代码会显得很重,并且每创建一个类组件,都要去继承一个React实例,至于Redux,更不用多说,很久之前Redux的作者就说过,“能用React解决的问题就不用Redux”,等等一系列的话。关于React类组件redux的作者又有话说

    大型组件很难拆分和重构,也很难测试。业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。组件类引入了复杂的编程模式,比如 render props 和高阶组件。 下面我们用类组件做一个简单的计数器 import React from 'react' class AddCount extends React.PureComponent { constructor(props){ super(props) this.state={ count: 0 } } addcount = () => { let newCount = this.state.count this.setState({ count: newCount +=1 }) } render(){ return ( <> <p>{this.state.count}</p> <button onClick={this.addcount}>count++</button> </> ) } } export default AddCount

    可以看出来,上面的代码确实很重。 为了解决这种,类组件功能齐全却很重,纯函数很轻便却有上文几点重大限制,React团队设计了React Hooks,React Hooks就是加强版的函数组件,我们可以完全不使用 class,就能写出一个全功能的组件

    什么是Hook:

    Hook是react 16.7新增的一个特性,主要是用来让无状态组件(函数式组件)可以使用状态,以前为了进行状态管理,需要使用类组件或者redux等来管理,相当于类组件中的state,只不过用useState去代替了,useState() = this.state(),setVal() = this.setState()

    Hooks’的单词意思为“钩子”。React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码”钩”进来。而React Hooks 就是我们所说的“钩子”。那么Hooks要怎么用呢?“你需要写什么功能,就用什么钩子”。对于常见的功能,React为我们提供了一些常用的钩子,当然有特殊需要,我们也可以写自己的钩子。

    下面是React为我们提供的默认的四种最常用钩子
    useState()useContext()useReducer()useEffect() 不同的钩子为函数引入不同的外部功能,我们发现上面四种钩子都带有`use前缀`, React约定,钩子一律使用 use前缀命名。所以,`你自己定义的钩子都要命名为useXXX`

    React Hooks的用法:

    深挖hook中常用的四个API

    useState():

    useState 是用来定义一个状态的,相当于类组件中的state,只不过用useState去代替了,它与类组件的状态不同,函数组件的状态可以是对象也可以是基础类型数据,

    useState返回的是一个数组,第一个是当前的状态值,第二个是对象,表明用于更改状态的函数(类似于setState)

    import React,{useState} from 'react'; import './App.css'; function App() { // useState创造1个状态,赋值一个初始值,当前赋值的初始值为0 // 数组的第一个是一个变量,此变量指向当前状态的值,相当于this.state // 数组的第二个是一个函数,次函数可以修改状态的值,相当于this.setState const [count,setCount] = useState(0); const addCount = ()=>{ setCount(count+10); } return ( <div className="App"> <div>{count}</div> <button onClick={addCount}>点击加1</button> </div> ); } export default App;

    注意: 设置值的时候必须返回一个新的值,可以使用…展开运算符辅助

    import React, {useState} from 'react' function App(){ let [obj,setObj] = useState({ name:"Gseg", age:12 }) return ( <div> <h2>{obj.name}</h2> //Object.assign(obj,{age:20}),这不算返回新值 <button onClick={()=>{ setObj({...obj,name:"111111"}) }}>set</button> </div> ) } export default App;

    特别的: 设置的API可以传入一个函数,当传入是一个函数的时候,参数默认为count现在的值

    import React, { useState } from 'react' function App() { let [count, setCount] = useState(10) let handleClick = () => { //x默认为count现在的值 setCount(x => { console.log(x) return x + 1 //返回的值即为修改的值 }) } return ( <div> {count} <button onClick={handleClick}>+1</button> </div> ) } export default App;

    useEffect():

    可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和componentWillUnmount 这三个函数的组合

    useEffect的使用大致可以分为四种情况:

    情况一: useEffect( ()=>{ } ) 只有第一个参数的时候 此时相当于componentDidMount + componentDidUpdate钩子的组合,初次渲染并且状态改变时都会触发

    情况二:useEffect(()=>{ },[loading]) 当有第二个参数(是一个非空数组)时,那么回调函数会依据数组的变化而调用 可以监听一些状态的改变

    情况三:useEffect(()=>{ },[]) 第二个参数写一个空数组 只在初次componentDidMount时执行一次

    情况四:useEffect(()=>{ return ()=>{ console.log("组件将要被卸载了") } }) useEffect中返回一个函数 此时相当于componentWillUnmount 钩子, 卸载组件的时候触发

    具体实例如下:

    import React,{ useEffect,useState } from 'react'; import './App.css'; function App() { const [loading,setLoading] = useState(true) //情况一: //只有第一个参数 // 相当于componentDidMount + componentDidUpdate useEffect(()=>{ console.log("11") setTimeout(() => { setLoading(false) }, 3000); }) //情况二: //当有第二个参数(是一个数组)时,那么回调函数会依据数组的变化而调用 //可以监听一些状态的改变 useEffect(()=>{ console.log("loading的值发生了改变") },[loading]) //情况三: //写一个空数组,只在初次componentDidMount时执行一次 useEffect(()=>{ console.log("初次渲染") },[]) //情况四 //useEffect中返回一个函数 //卸载组件的时候触发 useEffect(()=>{ return ()=>{ console.log("组件将要被卸载了") } }) let handleChange = ()=>{ setLoading(!loading) } return ( <div> {loading?<div>Loading...</div>:<div>内容加载完毕</div>} <button onClick={handleChange}>取反</button> </div> ) } export default App;

    useContext():

    如果想要组件之间共享状态,可以使用useContext。需要与createContext()配合使用React 的 Context API 是一种在应用程序中深入传递数据的方法,而无需手动一个一个在多个父子孙之间传递prop。当咱们需要的只是传递数据时,它可以作为像Redux这样的工具的一个很好的替代。 import React,{ useContext} from 'react'; import './App.css'; function App() { // useContext共享状态钩子 const AppContext = React.createContext() const Achild = ()=>{ const {name} = useContext(AppContext) return ( <div>这是组件a,使用的name值是:{name}</div> ) } const Bchild = ()=>{ const {name,age} = useContext(AppContext) return ( <div> <div>这是组件B,使用的name值是:{name}</div> <div>这是组件B,使用的age值是:{age}</div> </div> ) } return ( //共享{name:"zhangsan",age:21} 数据 <AppContext.Provider value={{name:"zhangsan",age:21}} > <Achild></Achild> <Bchild></Bchild> </AppContext.Provider> ); } export default App;

    useReducer():

    useReducer 和 redux 中的reducer 是一样的,说白了reducer就是个函数

    useReducer() 是个函数,有三个参数,第一个参数reducer,第二个参数初始值,第三个参数是init

    useReducer返回一个包含2个元素的数组,类似于useState。 第一个是当前状态,第二个是dispatch方法

    用法:const [state,dispatch] = useReducer(reducer,初始值)

    import React, { useReducer } from 'react'; import './App.css'; function App() { // useReducer const reducer = (state, action) => { switch (action.type) { case "add": return { ...state, count: state.count + 10 } case "sub": return { ...state, count: state.count - 10 } case "addCustom": //自定义加值 return { ...state, count: state.count + action.value } default: return state } } const [state, dispatch] = useReducer(reducer, { count: 10 }) const addCount = () => { dispatch({ type: 'add' }) } const subCount = () => { dispatch({ type: 'sub' }) } const addCountCustom = () => { dispatch({ type: 'addCustom', value:100 }) } return ( <div> <div>{state.count}</div> <button onClick={addCount}>点击+10</button> <button onClick={subCount}>点击-10</button> <button onClick={addCountCustom}>自定义加值</button> </div> ); } export default App;

    自定义hook:

    自定义钩子函数,因为react-hook的API都是以use开头的,所以按照规范,自定义的hook都以use开头去封装,便于阅读和区分,自定义hook,和普通的函数本质上没有区别,都是一些函数的封装,方便使用

    注意: 1.自定义hook,必须以use开头 2.自定义hook,可以使用我们这些hook,(useState,useEffect...)来封装

    这是一个简单的自定义HOOK例子demo:

    import React, {useState} from 'react' const useCus = (val, num) => { let [count, setCount] = useState(val) const add = () => { setCount(count + num) } return { count, add } } function App() { let { count, add } = useCus(10, 6) return ( <div> <h1>{count}</h1> <button onClick={() => { add() }}>+</button> </div> ) } export default App;

    除此之外,HOOK还有

    useCallback useMemo useRef

    等等。。。。。 但最常用的是上述四个API

    Processed: 0.016, SQL: 9