一:何为Hooks
以往React组件都是用class来编写,无状态组件才能用函数来写。Hooks允许在函数组件中使用特定的预定义的内部函数来标记组件的状态和生命周期,使得所有组件都可以用函数来编写
二:class组件的不足
1.状态逻辑复用难
①:缺少复用机制
②:渲染属性和高阶组件导致层级冗余
2.趋向复杂难以维护
①:生命周期函数混杂不相干的逻辑
②:相干逻辑分散在不同的生命周期
3.this指向困扰
①:内联函数过度创建新句柄(每次都是新的,会重新触发,导致子组件不停渲染)
②:类成员函数不能保证this
三:Hooks的优势
1.函数组件无this问题(都在函数内部,没有实例化的概念)
2.自定义Hook方便复用状态逻辑
3.副作用的关注点分离(不是发生在数据向视图转化之中,都是在之外的。例如:发起网络请求、访问原型上的DOM元素、写本地持久化缓存、绑定解绑事件都是数据渲染视图之外的。这些一般都是放在生命周期中的。useEffect都是在每次渲染完成之后调用)
四:Hooks的API
1.useState(必须按照固定的顺序和数量被调用,可以传入参数作为默认值)
const [state,updateStateFn] = useState() //返回两个参数 一个是当前的状态,还有一个是修改当前状态的函数
只能在顶层调用,不能在if语句,循环块中调用(可以使用eslint-plugin-react-hooks插件)
然后在package.json里面
"eslintConfig":{ "plugins":["react-hooks"], "rules":{"react-hooks/rules-of-hooks":"error"} }
动态传入参数
const [count,setCount] = useState(()=>{return props.defaultCount||0}) //延迟初始化,只会在初始化时候执行一次
2.useEffect(每次渲染之后调用)
1.副作用(在渲染过程之外):绑定事件、网络请求、访问DOM
副作用调用时机:Mount之后(componentDidMount)、update之后(componentDidUpdate)、Unmount(componentwillUnMount)之前
第一次调用时候相当于DidMount后续调用是componentDidUpdate
返回一个回调函数,在视图销毁之前触发,有两种销毁的原因:重渲染和组件卸载
第二个参数的传个数组:只有数组中的每个元素都不变的情况下,useEffect才不会执行(数组中的值变化,useEffect就执行)
如果不传数组,意味着每次渲染后都执行useEffect ,传入[],由于每次空数组都是相同的,所以只会在第一次渲染后执行
3.useContext
import React,{createContext,useContext} from 'react' var CountContext = createContext() function App(){ const count = useContext(CountContext ) return ( <CountContext.Consumer>{value=>{console.log(value)}}</CounterContext.Consumer> ) }
当然App组件调用时候要
<CountContext.Provider value={count}> <App/> </CountContext.Provider>
或者使用contextType 但是只能有一个context时候 有效
const count = this.context
4.useMemo(定义一段函数逻辑是否重复执行)
const count = useMemo(()=>{ return count*2 },[count])
memo定义一个无状态组件是否重复执行
两者本质都是使用同样的算法,来判定依赖是否改变,然后判断是否执行
和useEffect使用是一样的,区别在于调用时机:是在渲染时完成的
5.useCallback(解决子组件的值过度变化,导致子组件重复渲染的问题)
useMemo(()=>fn)
useCallback(fn)
如果useMemo的返回值是一个函数,那就可以简写成useCallback的形式
6.useRef(如果需要访问上一次渲染时候的数据,甚至state,就把它放在useRef中)
使用场景:获取子组件或者DOM节点的句柄、渲染周期之间共享数据的存储
class Counter extends PureComponent{ render(){ return (<h1>23</h1>)} } function App(){ const counterRef = useRef() const onClick =useCallback(()=>{ console.log(counterRef .current) }) return( <Counter onClick=onClick ref={counterRef }/> ) }
五:自定义Hooks组件(多个组件调用自定义hooks不会共享state)
//自定义组件 开头要以use function useCount(defaultCount){ const [count,setCount] = useState(defaultCount) return [count, setCount] }
除了可以返回一个数组,引用时候是标签,还可以返回一个JSX,引用时候是JS表达式
function useSize() { const [size, setSize] = useState({ document.documentElement.clientWidth, height: document.documentElement.clientHeight }); const onResize = useCallback(() => { setSize({ document.documentElement.clientWidth, height: document.documentElement.clientHeight }); }); useEffect(() => { window.addEventListener("resize", onResize); return () => { window.removeEventListener("resize", onResize); }; }, []); return size }
Hooks使用法则(开头都是use开头):①:仅仅在顶层调用hooks函数,不能在循环语句、嵌套语句中使用(影响调用顺序)
②:仅在函数组件和自定义hooks函数组件中调用(不能在其他普通函数中调用)
问题:1.生命周期函数如何映射到Hooks?
constructor:初始化state,使用useState
getDerivedStateFromProps:获取到state值后判断条件然后执行修改state的方法
shouldComponentUpdate:useMemo组件的使用
render:函数组件返回JSX,包含了render的能力
componentDidMount、componentDidUpdate、componentWillUnmont:useEffect是在渲染之后使用,返回一个clean 组件的方法,初次渲染是componentDidMount 第二次渲染是componentDidUpdate
componentDidCatch 和getDerivedStateFromError:目前hooks还不能支持
2.类实例成员变量如何映射到Hooks?
使用useRef,可以传入初始值参数,但是不能传入函数
class App ={ it =0 } function App(){ const it = useRef(0) }
3.Hooks中如何获取历史props和state?
function useC(){ const [count,setCount] = useState(0) const preCountRef = useRef() useEffect(()=>{ preCountRef.current = count }) const preCount = preCountRef.current return (<h1>Now:{count},Before:{preCount}</h1>) }
useRef不受每次重新渲染的影响
4.如何强制更新一个Hooks组件?
class组件中有forceUpdate生命周期,跳过了shouldComponetUpdate
创建不参与实际渲染的state,然后更新它的值,以此来实现强制重渲染
const [updater,setUpdater] = useState[0] function forceUpdate(){ setUpdater(updater=>updater+1) }