react hooks介绍

1.自变量(src):useState、useReducer、useContext
页面state很简单,可以直接使用useState
页面state比较复杂(state是一个对象或者state非常多散落在各处)请使用userReducer
页面组件层级比较深,并且需要子组件触发state的变化,可以考虑useReducer + useContext
2.因变量(e=mc):useMemo、useEffect、useCallback
3.第三方变量或者dom定位:useRef

1.1useState自定义变量,设置变量和修改变量的方法,初始值。
1.2如果说可以用 Context 来替代 useState() 的话,Reducer 可以替代事件方法,如果单独写三个方法会显得冗余,这时可以使用 Reducer 的 dispatch 机制。
useReducer是进阶版的useState,把多个useState合并为一个,redux中使用
例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。

//	reducer 计数器
const initialState = {count: 0};

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
};

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

1.3useContext跨级组件中使用,类似vue provide&inject
不论中间隔着几层,createContext(0) ->context.Provider ->useContext。

import React, { useState ,useContext, createContext} from 'react';

// 1.创建一个 context
const Context = createContext(0)

function App () {
  const [ count, setCount ] = useState(0)
  return (
    <div>
      点击次数: { count } 
      <button onClick={() => { setCount(count + 1)}}>点我</button>
// 2.使用CountContext.Provider包裹需要接收参数的子组件,并通过value传值
      <Context.Provider value={count}>
        {/* <Item1></Item1>
        <Item2></Item2> */}
        <Item3></Item3>
      </Context.Provider>
    </div>
    )
}
// 组件一, useContext 写法
function Item3 () {
// 3.创建子组件Counter,通过useContext把刚刚创建好的CountContext作为参数传进去,并读取count值
  const count = useContext(Context);
  return (
    <div>{ count }</div>
  )
}

export default App;


1.4context+consumer模拟redux
1.4.1.创建一个 context 对象 (React.createContext),
1.4.2.定义useReducer方法const [state, dispatch] = useReducer(loginReducer, initialData);
1.4.3.context.Provider订阅了这个 Context 对象的组件,value绑定reducer的dispatch方法,包裹下面子组件
1.4.3.需要消费的子组件用useContext接受dispatch方法,再调用

2.1useMemo和useCallback定义无副作用的因变量,useEffect是有副作用的因变量
useMemo 的函数会在渲染期间执行,所以可以使用useMemo解决渲染DOM期间会执行不相关函数的需求

2.2useMemo返回变量,useCallback返回函数
类组件,可以继承PureComponent
React.memo是一个高阶组件,useMemo是一个hook,共同点:它们都可以用来缓存数据,避免子组件的无效重复渲染。

import React, {useMemo, useRef} from "react";

function ReactMemoChild() {
    const ref = useRef(0);
    return (
        <>
            <p>页面渲染次数:{ref.current++}</p>
         </>
    );
}

export default React.memo(ReactMemoChild);

useMemo常用在以下两种场景的优化中:1)引用类型的变量   2)需要大量时间执行的计算函数。
const UseMemoDemo = () => {
    // 调用这个函数需要大量时间去计算
    const slowFunction = (number) => {
        console.log('calling slow function')
        for (let i = 0; i <= 1000000000; i++) {
        }
        return number * 2
    }
    const [inputNumber, setInputNumber] = useState(1)
    const [dark, setDark] = useState(true)
 
    // 场景1:执行某函数需要大量时间,使用useMemo来优化,在不必要执行函数的时候不执行函数
    const doubleNumber = useMemo(() => {
        return slowFunction(inputNumber)
    }, [inputNumber])
 
    // 场景2:每次组件更新会重新执行,内部的引用类型变量会重新创建,这会导致使用到引用类型变量的组件重新渲染,使用useMemo来让每次的变量相同
    const themeStyle = useMemo(() => {
        return {
            background: dark ? 'black' : 'white',
            color: dark ? 'white' : 'black'
        }
    }, [dark])
 
    useEffect(() => {
        console.log('themeStyle changed')
    }, [themeStyle])
    const handleChange = (e) => {
        setInputNumber(parseInt(e.target.value))
    }
    return (
        <>
            <input type='text' value={inputNumber} onChange={handleChange}/>
            <button onClick={() => {
                setDark(prevDark => !prevDark)
            }}>change theme
            </button>
            <p style={themeStyle}>{doubleNumber}</p>
        </>
    )
}
export default UseMemoDemo;

2.3useEffect、useMemo、useCallback中很多相似之处,如第二个参数的依赖属性一样。
都是闭包。需要释放内存
2.4 useEffect
useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
第二个参数3种情况:
a.不设置,任何改变都会触发第一个参数;
b.[],第一次空的时候执行,后面不执行。相当于componnetDidMount生命周期;
c.[参数名字],首次执行+数据变化时执行。相当于vue的watch,并且immediate为true
d.componentWillUnmount
const timer = setInterval(() => { setCount(count + 1) }, 1000) // useEffect方法的第一个参数是一个函数,函数可以return一个方法,这个方法就是在组件销毁的时候会被调用 useEffect(() => { return () => { clearInterval(timer) } }, [])

  • 使用useEffect时,若有多个副作用,则应该调用多个useEffect,而不是写在一个里面;
    比如请求数据,操作dom,修改标题都用useEffect
  • useEffect第一个参数可以返回一个函数,这个函数会在组件卸载时(也就是render了,生成新的快照时)执行,可以用来清除副作用里的操作;
  • useLayoutEffect是在render前同步执行的(和componentDidMount等价),useEffect是在render后异步执行的;
    useLayoutEffect相当于防抖,一般在useEffect执行有问题(多次执行),再用这个api
    useLayoutEffect与componentDidMount,componentDidUpdate的调用阶段是一样的;
import React, { useEffect, useLayoutEffect, useState } from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  const [state, setState] = useState("hello world")

  useEffect(() => {
    let i = 0;
    while(i <= 100000000) {
      i++;
    };
    setState("world hello");
  }, []);

  // useLayoutEffect(() => {
  //   let i = 0;
  //   while(i <= 100000000) {
  //     i++;
  //   };
  //   setState("world hello");
  // }, []);

  return (
    <>
      <div>{state}</div>
    </>
  );
}

export default App;

总结:1.优先使用 useEffect,因为它是异步执行的,不会阻塞渲染
2.会影响到渲染的操作尽量放到 useLayoutEffect中去,避免出现闪烁问题
3.useLayoutEffect和componentDidMount是等价的,会同步调用,阻塞渲染

因为 useEffect 是渲染完之后异步执行的,所以会导致 hello world 先被渲染到了屏幕上,再变成 world hello,就会出现闪烁现象。而 useLayoutEffect 是渲染之前同步执行的,所以会等它执行完再渲染上去,就避免了闪烁现象。也就是说我们最好把操作 dom 的相关操作放到 useLayouteEffect 中去,避免导致闪烁。

  • useEffect里面用定时器,会产生闭包陷阱。用useRef的ref.current存储变量
    闭包陷阱产生的原因就是 useEffect 等 hook 里用到了某个 state,但是没有加到 deps 数组里,这样导致 state 变了却没有执行新传入的函数,依然引用的之前的 state。

闭包陷阱的解决也很简单,正确设置 deps 数组就可以了,这样每次用到的 state 变了就会执行新函数,引用新的 state。不过还要注意要清理下上次的定时器、事件监听器等。

import { useEffect, useLayoutEffect, useRef } from 'react';

const App = () => {
    const [count,setCount] = useState(0);

    const fn = () => {
        //还可以做一些其他逻辑操作
        console.log(count);
    };
    
    const ref = useRef(()=>{});

   useEffect(() => {
        setInterval(() => {
            //最关键的一步,使用函数,接受一个旧的state,得到新的state
            //所以就会render
            setCount(count => count + 1);
        }, 1000);
    }, []);
    
    //每次在render前都给ref赋值新的fn,这个fn里的state是最新值
    useLayoutEffect(() => {
        ref.current = fn;
    });

    useEffect(() => {
        setInterval(() => ref.current(), 1000);
    }, []);

    return <div>count: {count}</div>;
}

export default App;

useEffect第二个参数如果是引用类型,发生变化监测不到,所以需要重写

const [a, setA] = useState({
b: 'dx',
c: '18',
})
const changeA = () => {
    setA((old) => {
    const newA = {...old}
    newA .b = 'yx'
    return newA 
    })
}
useEffect(() => {
/** 当组件挂载时执行一次changeA */
changeA ()
},[])
/**当changeA执行打印  {b:'yx',c:'18'}  */
useEffect(() => {
/** 执行逻辑 */
console.log(a)
},[a])

useRef扩展
我们知道useRef返回的就是一个普通的JS对象{current:undefined},所以我们直接创建一个js对象,也可以代替useRef()。

区别:

我们创建的对象,组件每次重新渲染都会创建一个新对象。如useR
useRef()创建的对象,可以确保每次渲染获取到的都是同一个对象。
当你需要一个对象不会因为组件的重新渲染而改变时,就可以使用useRef()。

  • useCallback返回函数,应该和React.memo配套使用,缺了一个都可能导致性能不升反而下降。
    给子组件传函数时,父组件用useCallback(function(){},deps), 子组件用React.memo包裹

React.useCallback(function helloWorld(){}, []);
// …功能相当于:
React.useMemo(() => function helloWorld(){}, []);

const handleMegaBoost = React.useMemo(() => {
  return function() {
    setCount((currentValue) => currentValue + 1234);
  }
}, []);


const handleMegaBoost = React.useCallback(() => {
  setCount((currentValue) => currentValue + 1234);
}, []);

但是,根据闭包的理解,useCallback传递的是一个闭包函数,所以存在一定的风险。除非是计算量特别大的子组件这种极端情况,否则不推荐使用。

在这里插入图片描述
useEffect触发两次引起的bug
在研究这个问题后得到了大概三种解决方案:
1.取消react.strictMode模式
2.在设置的参数的useEffect中加非空判断
3.把初始化放到useReducer里面
在这里插入图片描述

常见10个hooks
在这里插入图片描述

react生命周期
在这里插入图片描述
useId
import { useId } from ‘react’;

function PasswordField() {
const passwordHintId = useId(‘duan’);
}
无论有多少个PasswordField组件,ID都不一样

forwardRef()
useImperativeHandle
相当于vue defineExpose,暴露方法

import { forwardRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  useImperativeHandle(ref, () => {
    return {
      // ... your methods ...
    };
  }, []);
  // ...

react v16.8常用库

redux: 著名JavaScript状态管理容器

redux-thunk: 处理异步逻辑的redux中间件

immutable: Facebook历时三年开发出的进行持久性数据结构处理的库

react-lazyload: react懒加载库

better-scroll: 提升移动端滑动体验的知名库

styled-components: 处理样式,体现css in js的前端工程化神器(详情请移步我之前的文章styled-components:前端组件拆分新思路)

axios: 用来请求后端api的数据

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端段

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值