react hooks 和 react-redux hooks 应用场景

目前 ,Hooks 应该是 React 中最火的概念了,在阅读这篇文章之前,希望你已经了解了基本的 Hooks 是什么?

下面就介绍一下简单的使用场景

react hooks

useState

useState是react自带的一个hook函数,它的作用就是用来声明状态变量。 useState这个函数接收的参数是我们的状态初始值(initial state), 它返回了一个数组,这个数组的第[0]项是当前当前的状态值, 第[1]项是可以改变状态值的方法函数。


import React, { useState} from 'react';
function Example() {
     //useState的用法,分别是声明、读取、使用(修改)
    const [count, setCount] = useState(0) //数组结构
    const [age, setAge] = useState(18)
    const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
    return (
        <div>
            <button onClick={() => { setCount(count + 1) }}>count</button>
            <button onClick={() => { setAge(age + 1) }}>age</button>
        </div>
    );
}

export default Example;

复制代码

基于 useState 的用法,我们尝试着自己实现一个 useState


var _state; // 把 state 存储在外面
function useState(initialValue) {
  _state = _state | initialValue; // 如果没有 _state,说明是第一次执行,把 initialValue 复制给它
  function setState(newState) {
    _state = newState;
    render();
  }
  return [_state, setState];
}

复制代码

useEffect

使用useEffect,可以直接在函数组件内处理生命周期事件。 如果你熟悉 React class 的生命周期函数, 你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合

useEffect 使用需要注意的地方

  • 有两个参数 callback 和 dependencies 数组
  • 如果 dependencies 不存在,那么 callback 每次 render 都会执行
  • 如果 dependencies 存在,只有当它发生了变化, callback 才会执行
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';

function useEffectTest() {
  useEffect(() => {
      
    // 默认情况下,每次渲染后都会调用该函数
    console.log('render!');

    // 如果要实现 componentWillUnmount,
    // 在末尾处返回一个函数
    // React 在该函数组件卸载前调用该方法
    // 其命名为 cleanup 是为了表明此函数的目的,
    // 但其实也可以返回一个箭头函数或者给起一个别的名字。
    return function cleanup () {
        console.log('unmounting...');
    }
  })  
  return "useEffectTest";
}
复制代码

阻止每次重新渲染都会执行 useEffect 如果希望 effect 较少运行,可以提供第二个参数 - 值数组。 将它们视为该effect的依赖关系。 如果其中一个依赖项自上次更改后,effect将再次运行。

const [value, setValue] = useState(0);

useEffect(() => {
  // 仅在 value 更改时更新
  console.log(value);
}, [value]) 
复制代码

useContext

useContext 可以实现共享状态最大的改变是可以在使用 Counter 的时候不必在包裹 Children 了,比方说我们先创建一个上下文, 这个上下文里头有一个名为 count 的 state,以及一个修改 count 的方法 setCount

创建上下文


import React, { createContext, useState } from 'react';
import { Counter } from './Counter'
export const CountContext = createContext()
function Example2() {
    const [count, setCount] = useState(0)
    return (
        <div>
            <CountContext.Provider value={count,setCount}>
                <Counter />
            </CountContext.Provider>
        </div>
    )
}

export default Example2;

复制代码

使用上下文

import React, { useContext} from 'react';
import {CountContext} from './Example2'
export function Counter() {
    const {count,setCount} = useContext(CountContext)  //一句话就可以得到count
    return (
        <h2>{count}</h2>
        <button onClick={() => { setCount(count + 1) }}>count</button>
    )
}
复制代码

useReducer

Redux 的核心概念是,组件发出 action 与状态管理器通信。状态管理器收到 action 以后,使用 Reducer 函数算出新的状态

  • 简单来说 reducer是一个函数(state, action) => newState: 接收当前应用的state和触发的动作action,计算并返回最新的state。
import React, { useReducer } from 'react';
function reducer(state, action) {
    switch (action.type) {
        case 'add':
            return { count: state.count + 1 };
        case 'sub':
            return { count: state.count - 1 };
        default:
            return state
    }
}
function Example2() {
    const initialState = { count: 0 };
    const [state, dispatch] = useReducer(reducer,initialState)
    return (
        <div>
            <p>You clicked {state.count} times</p>
            <button onClick={() => { dispatch({ type: 'add'})}}>add</button>
            <button onClick={() => { dispatch({ type: 'sub'})}}>sub</button>
        </div>
    )
}

export default Example2;
复制代码

useMemo

React 16.8.0 中发布useMemo()

React.memo()是判断一个函数组件的渲染是否重复执行

useMemo()是定义一段函数逻辑是否重复执行

useMemo(() => fn, inputs)跟useCallback(fn, inputs)效果一样

const increase = useMemo(() => {
    if(value > 2) return value + 1;
}, [value]);
复制代码

useRef

useRef的作用: 获取DOM元素的节点 获取子组件的实例 渲染周期之间共享数据的存储(state不能存储跨渲染周期的数据,因为state的保存会触发组件重渲染)

import React, { useEffect, useRef } from 'react';
function App() {
  const h1Ref = useRef();
  useEffect(() => {
    console.log('useRef')
    console.log(h1Ref.current)
  }, [])
  return <h1 ref={h1Ref}>Hello World!</h1>
}
export default App;
复制代码

useCallback

  • 创建一个回调方法的缓存,可以让我们传入子节点作为props的时候,可以让其没有变化,避免没必要的渲染。

  • 根据输入的inputs,也就是一个数组,内部的内容是否又变回,决定是返回存储的老方法,还是返回新的方法并记录。

import React, { useState, useCallback, useEffect } from 'react';

const set = new Set();

function Callback() {
    const [count, setCount] = useState(1);
    const [val, setVal] = useState('');

    const callback = useCallback(() => {
        console.log(count);
    }, [count]);
    set.add(callback);


    return <div>
        <h4>{count}</h4>
        <h4>{set.size}</h4>
        <div>
            <button onClick={() => setCount(count + 1)}>+</button>
            <input value={val} onChange={event => setVal(event.target.value)}/>
        </div>
    </div>;
}

export default function Parent() {
    const [count, setCount] = useState(1);
    const [val, setVal] = useState('');

    const callback = useCallback(() => {
        return count;
    }, [count]);
    return <div>
        <h4>{count}</h4>
        <Child callback={callback}/>
        <div>
            <button onClick={() => setCount(count + 1)}>+</button>
            <input value={val} onChange={event => setVal(event.target.value)}/>
        </div>
    </div>;
}

function Child({ callback }) {
    const [count, setCount] = useState(() => callback());
    useEffect(() => {
        setCount(callback());
    }, [callback]);
    return <div>
        {count}
    </div>
}


复制代码

useImperativeMethods

接受一个ref作为参数,内部其实就是一个useLayoutEffect的调用。 主要就是在外部传入的ref上挂载内容 ,实现类似ref挂载到ClassComponent上的效果

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeMethods(ref, () : ({
    focus: () : {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);

复制代码

React-Redux Hooks

useSelector

  • useSelector用于从Redux存储的state中提取值并订阅该state,这基本上类似于在hooks中实现的mapStateToProps函数

  • 首先,不再提供ownProps API,并且应该使用useCallback或useMemo来通过自定义逻辑获取它们

  • 其次,useSelector()第二个参数也是依赖数组,跟useEffect一样。如果不提供第二个参数每次组件更新时都会重新计算;如果提供了依赖数组,只有依赖数组对应的值变更了之后,才会触发重新计算

  • 除此之外,redux 以前的性能优化逻辑同样保留了下来,如果当前的props跟老的props相同,则组件将不会重新渲染。

  • 由于React redux中使用的批处理更新的逻辑,导致同一组件中的多个useSelector()重新计算出state,只会让组件重新渲染一次。因此,我们可以自由的在组件中useSelector(),而不用担心重复渲染的情况

  • 在下面的例子中,我们可以将单个useSelector()分成两个独立的(一个读取title,另一个读取content)useSelector()他们在性能和渲染数量方面完全相同

import React from 'react';
import { useSelector } from 'react-redux';
 
const Component = props => {
  const { title, content } = useSelector(state => ({
      title: state.title, 
      content: state.content
  }));
  
  return <div title={title}>{content}</div>;
复制代码

useDispatch

  • 除了读取store中的state,还要能dispatch actions来更新store中的state,useDispatch就是这样一个API

  • 只要组件需要触发redux action,那么这个钩子就是你需要的。不幸的是,mapDispatchToProps 被废弃掉了 所以每当你想要dispatch action时,你需要使用dispatch(actionCreator())来调用它的action creator

  • 如果我们初次使用这种方式,会显得有点不太习惯,因为以前都是通过connect HOC来调用被包装成prop的dispatch函数

  • 但hooks的方式会为代码带来更多的清晰度。遗憾的是,如果我们想要在事件处理函数里面dispatch actions,必须创建一个匿名函数, 如:() => dispatch(actionCreator)。由于匿名函数的性质,这将在每次重新渲染时获得新的引用

  • 如果将这个匿名函数作为props传递给子组件组件,那么子组件将每次都重新渲染。为了优化性能,必须使该函数具有相同的引用, 解决方案是在useCallback中创建这个匿名函数。

import { useDispatch } from 'react-redux'
import { logout } from '../store/reducer'
    const dispatch = useDispatch()
    //登出
    const handleLogout = useCallback(() => {
        dispatch(logout())
    }, [dispatch])

猜你喜欢

转载自blog.csdn.net/sinat_17775997/article/details/124534365