React 基础hooks

190 阅读4分钟

1. Hooks

  1. useState 用于创建响应式数据 和 提供 更新数据的方法
const [data,setData]=useState()
1.在函数组件的一次执行上下文中 data 数据是不变的,也就是说setData 是异步更新
(除去在异步事件中,如定时器,promise.then 等),最新值是在
下次render时候才能获取到
总结:在未进入react调度流程中是同步的,进入react调度流程就是异步的
    解决办法:1. 使用useRef 2.使用useEffect 进行监视
    
2.如果两次setData 传递的值相同 那么组件不会重新更新
  1. useReducer 用于无状态组件中能够提供类似redux功能的api
const [a,setA]=useReducer((state,action)=>{
    const {d}=action
    if(d==='你好'){
        return state+1
    }else if(d==='我好'){
        return state+2
    }else {
        return state
    }
},0)
//使用方式
<button onClick={()=>setA({ name:'你好' })} >增加</button>
  1. useTransition 用于创建过渡任务,低优先级任务可以用它,例如 list列表渲染,实时搜索展示数据
例如在 tabs切换的时候 tabs是需要快速响应的,但是tabs的内容可能
不需要那么急迫,因为数据的问题 可能还会有一些loding效果,那么我们就可以把这个内容放入过渡任务中进行处理

假数据
const mockList1 = new Array(10000).fill('tab1').map((item,index)=>item+'--'+index )
const mockList2 = new Array(10000).fill('tab2').map((item,index)=>item+'--'+index )
const tab = { tab1: mockList1, tab2: mockList2 }

const [active,setActive]=useState('tab1') //需要快速响应的数据
const [data,setData]=useState(tab[active])//动态数据
const [isPending,startTransition]=useTransition() //用于创建过渡任务
更新方法
const changeTab=(value)=>{
    setActive(value) //立即更新
    //过渡更新-这个时候我们可以根据isPending值进行业务操作 比如 loding
    startTransition(()=>{
        setData(tab[value])
    })
}
<div className='tab' onClick={()=>changeTab('tab2')}> tab1 </div>
<div className='tab' onClick={()=>changeTab('tab1')}> tab2 </div>
<ul className='content' > 
{ isPending && <div> loading... </div> } 
{ data.map(item=> <li key={item} >{item}</li>) } 
</ul>
  1. useDeferredValue 和上面的类似,创建过渡任务,但它是创建一个状态
const [active,setActive]=useState('tab1') //需要快速响应的数据
const deferActive=useDeferredValue(active) //创建延时状态
const changeTab=(value)=>{
    setActive(value)
}
const data=tab[deferActive] //使用延时状态来更新数据 可以达到和 useTransition 同样的效果

2. hooks执行副作用

  1. useEffect 是异步调用,不会阻塞浏览器绘制视图 用于模仿生命周期 和监听某些数据的变化 进行一些列自定义的业务操作 在内部return一个函数则可以进行一些销毁清除事件等操作

  2. useLayoutEffect 是同步调用,会阻塞浏览器绘制视图 它是在浏览器绘制之前(方便修改dom) 可以避免浏览器的回流或者是重绘

  3. useInsertionEffect 它的执行时机比上面两个还要早,3>2>1 (速度), 这个hooks本质是为了解决CSS-in-JS 在渲染中注入样式的性能问题。其他场景react不期望用这个hooks

export default function Index(){ 
    React.useInsertionEffect(()=>{ 
    /* 动态创建 style 标签插入到 head 中 */ 
    const style = document.createElement('style') 
    style.innerHTML = ` .css-in-js{ color: red; font-size: 20px; } `
    document.head.appendChild(style) },[]) 
    return <div className="css-in-js" > hello , useInsertionEffect </div> 
   })
  }

3. hooks之状态获取和传递

  1. useContext 类组件共享数据的一种方式,比较鸡肋了现在
  2. useRef 获取dom 或者 保存状态。是同步方式
  3. useImperativeHandle 用于函数式组件 获取子组件实例的方法
import React, { useRef, useImperativeHandle, forwardRef } from 'react';

// 子组件
const ChildComponent = forwardRef((props, ref) => {
    const internalRef = useRef();

    // 使用 useImperativeHandle 定制对外暴露的内容
    useImperativeHandle(ref, () => ({
        // 这里定义了暴露给父组件的方法或属性
        // 例如,暴露一个方法
        focus: () => {
            internalRef.current.focus();
        },
        // 或者暴露一个属性
        value: internalRef.current.value,
    }));

    return <input ref={internalRef} />;
});

// 父组件
const ParentComponent = () => {
    const childRef = useRef();

    const handleClick = () => {
        // 使用子组件暴露的方法
        childRef.current.focus();
    };

    return (
        <div>
            <ChildComponent ref={childRef} />
            <button onClick={handleClick}>Focus Child Input</button>
        </div>
    );
};

4. hooks之状态派生和保存

  1. useMemo 用于缓存数据,提高性能 类似于计算属性 监听某个值的变化,执行回调函数,返回新值,如果第二个参数是空数组,那么它将在首次渲染的时候只执行一次,后续的值都不会变化。

    它的执行时机: useMemo -> render ->useEffect

const [count,setCount]=useState(0)
const cacheValue=useMemo(()=>{
    return `<div>${count}</div>`
},[count])
  1. useCallback 用于缓存函数,监听某个值的变化,返回一个新函数。一般用来配合React.memo 来缓存子组件的 但是在社区有过争论,认为useCallback 会产成额外的性能:对deps的判断。收益比较小。所以一些人认为就不用useCallback
子组件 必须要用React.memo 包裹,否则也缓存不了 React.memo是类似于class组件中的Pure.Component的作用
const DemoChildren = React.memo((props)=>{
    /* 只有初始化的时候打印了 子组件更新 */ 
    console.log('子组件更新') 
    useEffect(()=>{
            props.getInfo('子组件') 
        },[]) 
    return <div>子组件</div> 
   })
   
   父组件
 const DemoUseCallback=({ id })=>{ 
     const [number, setNumber] = useState(1) 
     const getInfo = useCallback((sonName)=>{ console.log(sonName) },[id]) 
     return <div> 
             {/* 点击按钮触发父组件更新 ,但是子组件没有更新 */} 
             <button onClick={ ()=>setNumber(number+1) } >增加</button> 
             <DemoChildren getInfo={getInfo} /> 
            </div> 
  }