React Hooks 的基本使用

一、基础 Hook

1. useState

函数式组件获得状态state的方式。

useState 返回一个数组,useState( "参数" ) ,参数即为初始化的值。

可以为 数组、对象、字符串、数字

import React, { useState } from 'react'
const [count, setCounnt] = useState(0);
function add() {
    // 第一种写法
    setCounnt(count + 1);
    // 第二种
    setCounnt(oldvalue => oldvalue + 1);
}

两种写法的区别:

  1. 使用第一种写法

    当进行多次setCount时,如:

    setCounnt(count + 1);
    setCounnt(count + 1);
    setCounnt(count + 1);
    

    最终count的值为1,因为setCount会进行合并,相当于只加了一次,和setState一样。

  2. 使用第二种写法时

    setCounnt(oldvalue => oldvalue + 1);
    setCounnt(oldvalue => oldvalue + 1);
    setCounnt(oldvalue => oldvalue + 1);
    

    最终count的值为 3 ,因为setCount使用函数形式,每次 oldValue 都是上一次setCount的值,进行三次,就是 0+3。

2. useEffect

函数式组件中完成类似于类组件中生命周期的功能,进行网络请求、事件监听等,引入 Effect Hook

可以定义多个 useEffect,第二个参数可以进行性能优化。

useEffect("回调函数",[依赖的属性])

2.1 不进行依赖限制

组件第一次加载就会执行和每次组件更新也会执行。

类似集成了componentDidMountcomponentDidUpdate

useEffect(()=>{
  // 代码块
})

2.2 不依赖任何 state

类似componentDidMount,只执行一次

useEffect(()=>{
  // 组件加载完成时执行这里的代码
}, [])

2.3 组件卸载前

类似 componentWillUnmount

useEffect(()=>{
  // xxxxxx代码块
  return ()=>{	
    // 相当于componentWillUnmount,组件卸载前调用
  }
}, [])

2.4 依赖特定 state

类似 componentDidUpdate

useEffect(()=>{
  // 当 count 改变,就执行代码
}, [count])

3. useContext

在类组件中创建: const myContext = React.createContext();

<MyContext.Provider value="hello">
	<Son />	
</MyContext.Provider>

在函数组件中使用:

// Son 函数组件
import React, { useContext } from 'react'

const text = useContext(TContext);// text 即为 hello

二、额外的 Hook

4. useRef

类如类组件中的 const myRef = React.createRef();

import React, { useRef } from 'react'

const myRef = useRef();

<input type="text" ref={myRef} />

使用:

import React, { useRef } from 'react'

// 函数子组件,接收ref需要用forwardRef包裹
const MyInput = React.forwardRef((props, ref) => {
  return <input type="text" placeholder="函数子组件" ref={ref}></input>
})

// 类子组件
class MyInput2 extends React.Component {
  render() {
    return (
      <div>
        <input type="text" ref={this.props.ref1} id="" placeholder="类子组件" />
      </div>
    )
  }
}

export default function RefHook() {
  const inpRef = useRef();
  const inpRef2 = useRef();

  function show() {
    inpRef.current.focus();
    inpRef2.current.value = "点击聚焦了";
  }
  return (
    <div>
      <MyInput ref={inpRef} />
      <MyInput2 ref1={inpRef2} />
      <button onClick={show}>聚焦</button>
    </div>
  )
}

5. useReducer

useReducer 不是 Redux 的某个替代品,而是useState的一种替代方案。当state处理逻辑复杂时,可以使用 useReducer 对其拆分。

const [state, dispatch] = useReducer(`reducer函数`, `initialState初始化state`, `init初始化函数`)
import React, { useReducer } from 'react'

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return state + 1;
    case "decrement":
      return state - 1;
    default:
      return state;
  }
}

export default function UseReducerDemo() {
  const [state, dispatch] = useReducer(reducer, 0);
  return (
    <div>
      <h2>当前计数:{state}</h2>
      <button onClick={e => dispatch({ type: "increment" })}>+1</button>
      <button onClick={e => dispatch({ type: "decrement" })}>-1</button>
    </div>
  )
}

6. useCallback

useCallback实际的目的是为了进行性能的优化。对返回函数进行优化

  • useCallback会返回一个函数的 memoized (记忆的)值;

  • 在依赖不变的情况下,多次定义的时候,返回的值是相同的;

// useCallback(fn, deps) 相当于 useMemo(() => fn, deps)
const memoizedCallback = useCallback(
  () => { 
    dosomething(a, b);
  },
  [a, b]
);

使用场景:

在将一个组件中的函数,传递给子组件进行回调使用时,使用 useCallback 对函数进行处理,就不会使使用该函数的子组件重新渲染,进而得到性能优化。

import React, { useState, useCallback, memo } from 'react'

const MyButton = memo((props) => {
  console.log(props.title + "重新加载");
  return <button onClick={props.increment}>{props.title}</button>
})

export default function CallbackHook() {
  console.log("CallbackHook重新渲染");
  const [count, setCount] = useState(0);
  const [num, setNum] = useState(0);
  function increment1() {
    console.log("执行increment1函数");
    setCount(count + 1);
  }
  // 使用 useCallback,依赖 count
  const increment2 = useCallback(() => {
    console.log("执行increment2函数");
    setCount(count + 1);
  }, [count]);
  
  // 使用 useMemo 实现 useCallback。
  const increment3 = useMemo(() => {
    return () => {
      console.log("执行increment3函数");
      setCount(count + 1);
    }
  }, [count]);
    
  return (
    <div>
      <h2>CallbackHook</h2>
      <p>count的值是:{count}</p>
      <p>num的值是:{num}</p>
      {/* <button onClick={increment1}>+1</button> */}
      {/* <button onClick={increment2}>+1</button> */}
      <MyButton title="increment1" increment={increment1} />
      <MyButton title="increment2" increment={increment2} />
      <button onClick={e => setNum(num + 5)}>改变num</button>
    </div>
  )
}

memo函数:当传递的 props 中参数值不改变,就不重新渲染函数组件,类比等于类组件的 PureComponent

使用useCallback处理increment2函数,导致当count值不变时,函数也不会被重新创建,传递给MyButton组件的函数props也不变。所以MyButton组件不重新渲染。

image-20211112111833133

7.useMemo

useMemo实际的目的也是为了进行性能的优化。对返回值进行优化

  • useMemo返回的也是一个 memoized (记忆的)值;
  • 在依赖不变的情况下,多次定义的时候,返回的值是相同的;
const memoizedValue = useMemo(()=> computeExpensiveValue(a, b), [a,b])

使用场景:

1. 复杂计算

import React, { useMemo, useState } from 'react'

function computeCountSum(num) {
  console.log("computeCountSum函数执行了");
  let sum = 0;
  for (let i = 0; i <= num; i++) {
    sum += i;
  }
  return sum;
}
 
export default function MemoHook() {
  const [count, setCount] = useState(10);
  const [flag, setFlag] = useState(true);
  // 无论count或flag状态改变, 函数也会重新执行并给sum赋值
  // const sum = computeCountSum(count);

  // 只有当 count 的值改变时,才会重新求 sum 的值
  const sum = useMemo(() => computeCountSum(count), [count])

  return (
    <div>
      <h2>1----count的累加和是:{sum}</h2>
      <h2>flag: {flag ? "true" : "false"}</h2>
      <button onClick={e => setCount(count + 1)}>+1</button>
      <button onClick={e => setFlag(!flag)}>flag切换</button>
    </div>
  )
}

2. 传入子组件

import React, { memo, useMemo, useState } from 'react'

// memo 自身依赖改变才重新渲染组件
const PersonInfo = memo((props) => {
  console.log("PersonInfo重新渲染");
  return (<h2>名字是:{props.info.name} 年龄是:{props.info.age}</h2>)
})

export default function MemoHook() {
  const [flag, setFlag] = useState(true);
  // 当 flag 状态改变时,info会被重新赋值,子组件也会被重新渲染
  // const info = { name: "wht", age: 18 }

  // flag 改变,子组件不会重新渲染。因为传入 info 对象不变
  // const [info, setInfo] = useState({ name: "wht", age: 18 });
  const info = useMemo(() => ({ name: "wht", age: 18 }), [])

  return (
    <div>
      <PersonInfo info={info} />
      <h2>flag: {flag ? "true" : "false"}</h2>
      <button onClick={e => setFlag(!flag)}>flag切换</button>
    </div>
  )
}

8. useImperativeHandle

useImperativeHandle 可以让你在使用 ref自定义暴露给父组件的实例值。而不像使用 ref 直接暴露整个标签元素。

useImperativeHandle 应当与 forwardRef一起使用:

import React, { useRef, forwardRef, useImperativeHandle } from 'react';

// 函数子组件
const MyInput = forwardRef((props, ref) => {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }), [inputRef]);

  return <input ref={inputRef}></input>
})

export default function UseImperativeHandle() {
  const inpRef = useRef();

  return (
    <div>
      <MyInput ref={inpRef} />
      <button onClick={e => inpRef.current.focus()} > 聚焦</button>
    </div>
  )
}

三、自定义Hook

自定义 Hook ,完成对重复代码的整合。

可以使用 react 的 hook 方法

import React, { useState, useEffect } from 'react'
import qs from 'querystring'
export default function Zidingyi() {
  // 2. 使用自定义hook:当成普通hook语法使用就行
  const data = useFetchGet('http://localhost:3000/data.json')
  // console.log(data)
  return (
    <div>
      <h1>自定义hook</h1>
      <p>用户名:{data.user}</p>
      <p>年龄:{data.age}</p>
    </div>
  )
}

//  1. 定义 自定义 hook
function useFetchGet(url, CS) {
  const [data, setData] = useState({});
  if (typeof CS == 'object') {
    CS = qs.stringify(data);
  }
  useEffect(() => {
    fetch(url + '?' + CS).then(res => res.json()).then(res => { setData(res) })
  }, [url, CS])
  return data
}

redux中的 hook

1. useSelector

用于react 函数组件获取 redux 中的 state 。

const state1 = useSelector(state => state.state)
// state1 的值 即为 useSelector第一个回调函数的返回值

// 注意:第二个参数用来性能优化。默认当redux中数据改变会进行旧数据和新数据的===比较,
// 如果你第一个回调函数返回的是对象格式(每一次对象地址都不同),那么必然重新渲染,
// 尽管当前组件不依赖发生变化的数据。当返回的是基本数据类型,那可以不用添加第二个参数进行性能优化。
import { useSelector } from 'react-redux'

const { topBanners } = useSelector(state => ({
  topBanners: state.recommend.topBanners
}));

<span>长度:{topBanners.length}</span>

性能优化:

import { shallowEqual, useSelector } from 'react-redux'

const { topBanners } = useSelector(state => ({
  topBanners: state.topBanners
}), shallowEqual);

2. useDispatch

用于 react 函数组件获取 redux 中的 dispatch 方法,来操作数据。

import { useDispatch } from 'react-redux'

const dispatch = useDispatch();

useEffect(() => {
  dispatch(getTopBannersAction());
}, [dispatch]);
posted @ 2021-11-12 20:31  青柠i  阅读(137)  评论(0编辑  收藏  举报