React Hooks性能优化

memo、useMemo、useCallback的使用

memo

定义:React.memo 仅检查 props 变更。 当props变更时,及渲染组件(不管子组件是否依赖props)。针对子组件不采用父组件的参数时,则可以采用memo来减少子组件的渲染。达到性能优化。

直接举一个栗子:

import React,{useState} from 'react';
const Child = (props) => {
    console.log('子组件渲染')
    return(
        <div>子组件</div>
    );
}
export default () => {
    const [count, setCount] = useState(0);
    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>+</button>
            <p>计数器:{count}</p>
            <Child/>
        </>
    )
}
复制代码

效果图如下:
image.png
效果:在每次父组件状态更新时渲染页面,子组件也会跟着渲染。
image.png

使用memo

import React,{useState,memo} from 'react';
const Child = (props) => {
    console.log('子组件渲染')
    return(
        <div>子组件</div>
    );
}
const ChildMemo = memo(Child);
export default () => {
    const [count, setCount] = useState(0);
    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>+</button>
            <p>计数器:{count}</p>
            {/* <ChildMemo count={count}/> */}
            <ChildMemo/>
        </>
    )
}
复制代码

image.png

可以根据子组件是否引用父组件传递的值是否改变来进行渲染子组件。
ps:此方法仅作为性能优化的方式而存在。但请不要依赖它来“阻止”渲染,因为这会产生 bug。

useMemo

useMemo: 把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。

如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。

useMemo(() => fn, deps) 相当于 useCallback(fn, deps)

上栗子:

import React,{useState,useMemo} from 'react';
const Child  = function (props) {
    const {info} = {...props}
    console.log(`子组件接收: ${info.age}`)
    return (
        <div>
            显示子组件
        </div>
    )
}

export default () => {
    const [age, setAge] = useState(6)
    const [sex, setSex] = useState('boy')
    const info = useMemo(() => {
        return (
            {
                name: 'echo',
                age: age,
            }
        )
    }, [age])
    return(
        <div>
            <button onClick={() => {setAge(age => age + 1)}}>年龄+1</button>
            <button onClick={() => {setSex(sex => sex === 'boy' ? 'girl' : sex)}}>改变性别</button><br></br>
            <div>
                {
                    `姓名:${info.name}
                     年龄:${info.age}
                     性别:${sex}
                    `
                }
            </div>
            <Child info={info}></Child>
        </div>
    )
}
复制代码

初次渲染效果图

image.png
进行年龄单击事件++ 性别单击事件+ 效果图:

image.png
useMemo 创建函数,并且有依赖数组,依赖数组中的状态变化时,进行渲染

useCallback

useCallback:把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

上栗子:

import React,{useState,,useCallback} from 'react';
function Child({onClick,number}){
    console.log('子组件渲染')
    return (
        <button onClick={onClick}>{number}</button>
    )
}
Child = memo(Child);
export  default  function Count(){
    const [name,setName]= useState('计数器');
    const [number,setNumber] = useState(0);
    
    const addClick = useCallback(()=>{
        setNumber(number+1);
    },[name]); //根据依赖数组中的数据是否变化进行渲染 为空则直接重新渲染
    console.log('父组件渲染')
    return (
        <>
            <input type="text" value={name} onChange={(e)=>setName(e.target.value)}/>
            <Child number={number} onClick={addClick}/>
        </>
    )
}
复制代码

其余Hooks学习。直接代码搬运工。虽然搬运->建议照着敲一遍更能够理解和帮助记忆。

useReducer

useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。

上官网文档栗子

import React,{useState, useReducer} from 'react';
const initialState = 0;

function 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 init(initialState) {
    console.log(initialState)
  return {count: initialState};
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState, init);
  //useReducer第三个参数可以对初始值进行重新构造(文档中称为:惰性地创建初始 state)
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}
复制代码

useContext

直接上官方栗子:

import React,{useState, useContext} from 'react';
const themes = {
    light: {
      foreground: "#000000",
      background: "#f3f3f3"
    },
    dark: {
      foreground: "#ffffff",
      background: "#222222"
    }
  };
  
  const ThemeContext = React.createContext(themes.light);
  
function App() {
    return (
      <ThemeContext.Provider value={themes.dark}>
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
  
  function Toolbar(props) {
    return (
      <div>
        <ThemedButton />
      </div>
    );
  }
  
  export default function ThemedButton() {
    const theme = useContext(ThemeContext);
    console.log(theme)
    return (
      <div style={{ background: theme.background, color: theme.foreground, width: 80, height: 80 }}>
        I am styled by theme context!
      </div>
    );
  }
复制代码

效果图

image.png

useRef

上栗子:

import React,{useState, useRef} from 'react';

export default function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    // inputEl.current.focus();
    inputEl.current.value = 666
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>place onClick</button>
    </>
  );
}
复制代码

和Vue的ref访问一样

未单击效果图:

image.png

单击后效果图:

image.png

请记住,当 ref 对象内容发生变化时,useRef 并不会通知你。变更 .current 属性不会引发组件重新渲染。如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调 ref 来实现

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享