React Hooks 学习总结

前言

最近的换写React项目了,好久没有写React,还动不动就想class🤣,现在跟上时代,重新学习React Hook.Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

为什么有Hook?

1、在组件之间复用状态逻辑比较难,需要用到高阶组件或者render props,Hook可以无需修改组件结构的情况下,复用状态逻辑。
2、Hook可以将组件中相互关联的部分拆分成更小的函数,方便理解;
3、不再使用class和this,更方便上手和理解;

使用Hook的注意事项

1、只能在最外层函数中调用Hook。(不要在循环、条件判断、子函数中调用)
React在创建hook时,以链表的解构存储,前一个hook中next指针指向下一个hook。在组件更新时会进行hook的一一对应的比对,所以要保证每次hook数量一致,位置相同。循环、条件判断中使用hook,会导致数量和位置的变化,产生错误。
2、只在react的函数组件中调用。

常用的HooK

1.useState

  • 作用:在组件内部添加一个state,React会在重复渲染时保留这个state。
  • 参数: 1个,state的初始值initialState;
const [state,setState] =useState(initialState)
  • 返回值 :2个
    1)state 当前状态,在初始渲染时,state就是initialState
    2)setState更新当前state的函数,他接受一个新的state,并且将该组件的一次渲染加入队列,他有点类似类组件中this.setState,但并不会将新旧state合并
//在函数组件中
const [state,setState] =useState({name:"lily",age:18})
return(
<button onclick={setState({age:20})}>改变年龄<button>
//这里使用useState不会将新旧state,现在state为{age:20},name属性就没有了
//所以需要注意 onclick={setState({...state,age:20})}
)
  • 特点
    1) 每次渲染都是独立的闭包,拥有当前自己的props\state\函数
    2)函数式更新
//如果新的状态需要依赖旧的状态,可以使用函数式更新
setState(state =>({...state,age:state.age +1}))
//将旧状态传入,返回新的状态

3)懒加载初始化state

//如果state中的值需要复杂计算
const [state,setState] =useState(()=>  ({name:"lily",age:12}))
  • 性能优化(减少渲染次数)
    1)Object.is
    在调用state的更新函数时,React会先进行Object.is的比较算法
    如果state不相同,进行子组件渲染和effect,否则不进行刷新渲染

2)useMemo(缓存值)
某些情况下组件中的值需要依赖state进行计算后得到,由于每次组件更新都会去重新计算,为了减少性能消耗,可以使用useMemo缓存值,仅仅当依赖改变时,才重新计算。

  • 参数:计算函数,依赖数组
    则函数仅会在某个依赖项改变时才重新计算memoized 值。
 const [count, setCount] = useState(0);
//expensive 需要依赖count计算得到
 const expensive = useMemo(() => {
 let sum = 0;
 for (let i = 0; i < count; i++) {
 sum += i;
 }
 return sum;
 //只有count变化,这⾥才重新执⾏
 }, [count]);

3)useCallback (缓存回调函数)
由于每次组件更新都会内部函数都会重新创建一次,为了节省性能,可以使用useCallback,使内联回调函数仅仅在传入依赖项改变时进行更新。

  • 参数:内联回调函数,依赖数组
    则函数仅会在某个依赖项改变时才进行更新。
 const [count, setCount] = useState(0);
 const addClick = useCallback(() => {
setCount(count+1)
 }, [count]);

4)memo (缓存组件)
父组件更新子组件也会重新创建,为节省性能,可用memo将子组件缓存,仅仅当props改变时,组件才进行更新

const Child = (props)=>{
return <div>{props.age}</div>
}
//这里只会在props改变的时候重新渲染
Child =memo(Child)
const Father=()=>{
const [name,setName]=useState("lily")
const [age,setAge]=useState(18)
return(
<>
<button onclick={setAge(age+1)}>改变年龄<button>
<input onChange={e=>(setName(e.target.value))}>改变姓名<button>
<Child age={age}/> 
</>
)
}

5)useRef (缓存ref)
同理,用useRef可对React.creactRef对象进缓存

2. useReducer

  • 作用:编写自定义hook或者在某些改变state逻辑复杂的情况下替代useState,用法类似redux,useState是useReducer的语法糖。
  • 参数: 3个,reducer方法 、initialState初始值,init;
const [state,dispatch] =useReducer(reducer ,initialState)
  • 返回值 :2个
    1)state当前状态,在初始渲染时,state就是initialState
    2)dispatch派发方法,接受参数action,改变当前state,并且将该组件的一次渲染加人队列。

  • 自定义hook

//实现一个useState
const useState=(initalState)=>{
    const reducer = useCallback(_state,action)=>{
        return action.paylaod
    }
    const [state,dispatch] = useReducer(reducer, initalState)
    const setState = (state)=>{
        dispatch({paylaod:state})
    }
    return [state,setState]
}

3.useContext

  • 作用:提供更方便的方法从React上下文中拿到consumer的值
    参数: 1个,context(React.createContext);
const  value=useContext(context)
  • 返回值 :1个 consumer的值

4.useEffect

  • 作用:在React渲染阶段,改变DOM、添加订阅、设置定时器等操作会破坏UI一致性,含有副作用的操作将不被允许,使用useEffect完成副作用的操作,传入函数会在组件渲染后执行.
  • 参数: 2个,didUpdate包含副作用的回调函数,在组件渲染后执行,[state]依赖项,传入依赖项,仅在依赖项改变后的执行
useEffect(didUpdate,[state])
  • 返回值 :1个 清除副作用函数,在组件销毁前执行,相当于componentWillUnmount,可进行定时器,事件监听的移除操作
  • 特点
    由于useEffect的副作用函数在组件渲染后执行,他和class组件中的 生命周期componentDidMount/componentDidUpdate和componentWillUnmount有相同的用途

5.useRef、useImperativeHandle

  • useRef作用:对ref对象进缓存,ref 对象在组件的整个生命周期内保持不变。
  • 参数: 1个,initialValue,ref.curren初始化的值
function  FocusButton() {
    const inputEl = useRef(null); 
    const onButtonClick = () => {
        // `current` 指向已挂载到 DOM 上的文本输入元素
        inputEl.current.focus();
    };
    return (
        <>
            <input ref={inputEl} type="text" />
            <button onClick={onButtonClick}>Focus the input</button>
        </>
    );
}

  • useImperativeHandle作用:有些情况下我们需要父组件把ref传递给子组件,仅仅使用forwardRef将子组件的ref属性都暴露给父组件是危险的,也违反了封装原则,useImperativeHandle允许在子组件中把自定义实例附加到父组件传过来的ref上,减少暴露给父组件的属性
  • 参数: 3个,ref, createHandle处理函数,其返回值会替换父组件传来的ref.current, [state]依赖项
useImperativeHandle(ref, createHandle, [state])
  • 例子
let Child =  (props, ref) =>{
    const inputRef = useRef();
    useImperativeHandle(ref, () => ({
        focus: () => {
            inputRef.current.focus()
        }
    }));
    return <input ref={inputRef} />;
}
Child = forwardRef(Child );

const Father=() => {
    const ref = useRef(null);
    return (
        <>
            <Child ref={ref}/>
             <button onClick={()=>{ ref.current.focus()}}>聚焦</button >
        </>
    )
}

6.useLayoutEffect

  • 作用 :与useEffect相同,都是用来执行副作用。
  • 特点 :useEffect是在渲染之后调用effect,也就是下一桢执行,是异步的;
    而useLayoutEffect在当前帧Render tree计算布局(Layout)信息后,渲染前,同步调用effect。官方文档建议使用标准useEffec。

7.自定义Hook

  • 定义:函数以use开头并且在函数内部调用其他hooks,并且返回一个数组,可以实现逻辑复用。
  • 例子 :
//实现一个thunk
const useThunk =(reducer,initalState)=>{
const [state,dispatch] =useReducer(reducer,initalState)
const thunkDispatch = (action)=>{
if( action instanceof Function){ //如果action是函数,传入新的dispatch执行action
   return action (thunkDispatch,()=>state)
}else{
dispatch(action )
}
return [state,thunkDispatch]
}


结尾

现在就只了解到这么多。还有什么没写到的日后更新。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335