您现在的位置是:网站首页> 编程资料编程资料
在 React 项目中全量使用 Hooks的方法_React_
2023-05-24
329人已围观
简介 在 React 项目中全量使用 Hooks的方法_React_
前言
此篇文章整理了在 React 项目开发中常用的一些 Hooks
React Hooks
Hooks 只能用于函数组件当中
useState
import { useState } from 'react'; const Component = () => { const [count, setCount] = useState(0); return ( ) } 此方法会返回两个值:当期状态和更新状态的函数。效果同 this.state 与 this.setState,区别是 useState 传入的值并不一定要对象,并且在更新的时候不会把当前的 state 与旧的 state 合并。
useReducer
useReducer 接收两个参数,第一个是 reducer 函数,通过该函数可以更新 state,第二个参数为 state 的初始值,是 useReducer 返回的数组的第一个值,也是在 reducer 函数第一次被调用时传入的一个参数。
基础用法
import { useState } from 'react'; const Component = () => { const [count, setCount] = useState(0); return ( ) } 在基础用法中,返回一个 dispatch 通过 dispatch 触发不同的 action 来加减 state。这里既然能传string action 那么肯定也能传递更复杂的参数来面对更复杂的场景。
进阶用法
import { useReducer } from 'react'; const Component = () => { const [userInfo, dispatch] = useReducer( (state, { type, payload }) => { switch (type) { case 'setName': return { ...state, name: payload }; case 'setAge': return { ...state, age: payload }; } }, { name: 'Jace', age: 18 } ); return ( ); }; useContext
在上述案例 useReducer 中,我们将函数的参数改为一个对象,分别有type和 payload 两个参数,type 用来决定更新什么数据,payload 则是更新的数据。写过 react-redux 的同学可能发这个 reducer 与 react-redux 中的 reducer 很像,我们借助 react-redux 的思想可以实现一个对象部分更改的 reducer ,那么我们便可以使用 React Hooks 的 useContext 来实现一个状态管理。
import { useMemo, createContext, useContext, useReducer } from 'react'; const store = createContext([]); const App = () => { const reducerValue = useReducer( (state, { type, payload }) => { switch (type) { case 'setName': return { ...state, name: payload }; case 'setAge': return { ...state, age: payload }; } }, { name: 'Jace', age: 18 } ); const [state, dispatch] = reducerValue; const storeValue = useMemo(() => reducerValue, reducerValue); return ( ); }; const Child = () => { const [state, dispatch] = useContext(store); // 在子组件中使用 console.log(state); return ( ); } useEffect
import { useState, useEffect } from 'react'; let timer = null; const Component = () => { const [count, setCount] = useState(0); // 类似于 class 组件的 componentDidMount 和 componentDidUpdate: useEffect(() => { document.title = `You clicked ${count} times`; timer = setInterval(() => { // events ... }, 1000) return () => { // 类似 componentWillUnmount // unmount events ... clearInterval(timer); // 组件卸载、useEffect 更新 移除计时器 }; }, [count]); // ... } 如果 useEffect 第二个参数数组内的值发生了变化,那么useEffect第一个参数的回调将会被再执行一遍,这里要注意的useEffect 的返回值函数并不只是再组件卸载的时候执行,而是在这个 useEffect 被更新的时候也会调用,例如上述 count 发生变化后,useEffect 返回的方法也会被执行,具体原因见Using the Effect Hook – React (reactjs.org)
useLayoutEffect
useLayoutEffect 与 useEffect 的API相同,区别:useEffect 在浏览器渲染后执行,useLayoutEffect 在浏览器渲染之前执行,由于JS是单线程,所以 useLayoutEffect 还会阻塞浏览器的渲染。区别就是这,那么应用场景肯定是从区别中得到的,useLayoutEffect 在渲染前执行,也就是说我们如果有状态变了需要依据该状态来操作DOM,为了避免状态变化导致组件渲染,然后更新 DOM 后又渲染,给用户肉眼能看到的闪烁,我们可以在这种情况下使用 useLayoutEffect。
当然这个不只是状态的改变,在任何导致组件重新渲染,而且又要改变
DOM的情况下都是useLayoutEffect的使用场景。当然这种场景不多,useLayoutEffect也不能多用,且使用时同步操作时长不能过长,不然会给用户带来明显的卡顿。
useRef
细心的同学有可能发现我在上面写 useEffect 中有一个 timer 变量,我将其定义在了函数组件外面,这样写简单使用是没问题的,但是如果该组件在同一页面有多个实例,那么组件外部的这个变量将会成共用的,会带来一个冲突,所以我们需要一个能在函数组件声明周期内部的变量,可以使用 useState 中的 state 但是 state 发生变化组件也会随之刷新,在有些情况是不需要刷新的,只是想单纯的存一个值,例如计时器的 timer 以及子组件的 Ref 实例等等。
import React, { useRef, useState, useEffect } from 'react'; const Compnent = () => { const timer = useRef(null); const [count, setCount] = useState(0); useEffect(() => { clearInterval(timer.current); timer.current = setTimeout(() => { setCount(count + 1); }, 1000); }, [count]); return UseRef count: {count}; } useRef 只接受一个参数,就是 初始值,之后可以通过赋值 ref.current 来更改,我们可以将一些不影响组件声明周期的参数放在 ref 中,还可以将 ref 直接传递给子组件 子元素。
const ref = useRef();Hello// or
或许有同学这时候会想到,当子组件为 Class 组件时,ref 获取的是 Class 组件的实例,上面包含 Class 的所有方法属性等。但当子组件为 Function 组件时,ref 能拿到什么,总不可能是 function 内定义的方法、变量。
useImperativeHandle
import React, { useRef, useState, useImperativeHandle } from 'react'; const App = () => { const ref = useRef(); return ( ); }; const Child = React.forwardRef((props, ref) => { const inputRef = useRef(); const [value, setValue] = useState(1); useImperativeHandle(ref, () => ({ value, // 内部变量 setValue, // 方法 input: inputRef.current // Ref })); return ( ); }) 使用 useImperativeHandle 钩子可以自定义将子组件中任何的变量,挂载到 ref 上。React.forwardRef 方法可以让组件能接收到 ref ,然后再使用或者透传到更下层。
useCallback
import React, { useCallback } from 'react'; const Component = () => { const setUserInfo = payload => {}; // request api const updateUserInfo = useCallback(payload => { setUserInfo(Object.assign({}, userInfo, payload)); }, [userInfo]); return ( ) } useCallback 会在二个参数的依赖项发生改变后才重新更新,如果将此函数传递到子组件时,每次父组件渲染此函数更新,就会导致子组件也重新渲染,可以通过传递第二个参数以避免一些非必要性的渲染。
useMemo
import React, { useMemo } from 'react'; const Component = () => { const [count, setCount] = useState(0); const sum = useMemo(() => { // 求和逻辑 return sum; }, [count]); return {sum} } useMemo 的用法跟 useCallback 一样,区别就是一个返回的是缓存的方法,一个返回的是缓存的值。上述如果依赖值 count 不发生变化,计算 sum 的逻辑也就只会执行一次,从而性能。
React Redux Hooks useSelector
import { shallowEqual, useSelector } from 'react-redux'; const Component = () => { const userInfo = useSelector(state => state.userInfo, shallowEqual); // ... } useSelector 的第二个参数是一个比较函数,useSelector 中默认使用的是 === 来判断两次计算的结果是否相同,如果我们返回的是一个对象,那么在 useSelector 中每次调用都会返回一个新对象,所以所以为了减少一些没必要的 re-render,我们可以使用一些比较函数,如 react-redux 自带的 shallowEqual,或者是 Lodash 的 _.isEqual()、Immutable 的比较功能。
useDispatch
import React, { useCallback } from 'react'; import { useDispatch } from 'react-redux'; const Compnent = () => { const dispatch = useDispatch(); const clearUserInfo = useCallback( () => dispatch({ type: 'clearUserInfo' }), [dispatch] ); return ( 使用
dispatch 来调度操作,加上useCallb
相关内容
- Vue中使用 Echarts5.0 遇到的一些问题(vue-cli 下开发)_vue.js_
- 从Vue转换看Webpack与Vite 代码转换机制差异详解_vue.js_
- npm安装windows-build-tools卡在Successfully installed Python2.7_node.js_
- next.js初始化参数设置getServerSideProps应用学习_javascript技巧_
- vue和iview结合动态生成表单实例_vue.js_
- Vue enter回车导致页面刷新问题及解决_vue.js_
- 新建的React Native就遇到vscode报警解除方法_React_
- react hooks实现原理解析_React_
- vue2项目中全局封装axios问题_vue.js_
- Vue项目中安装使用axios全过程_vue.js_
