react:一文搞清楚hook到底是个啥 您所在的位置:网站首页 mememe是个啥 react:一文搞清楚hook到底是个啥

react:一文搞清楚hook到底是个啥

2024-06-14 13:00| 来源: 网络整理| 查看: 265

文章目录 为什么出现hook解决的问题Hook简介简单的例子声明多个 state 变量内置的hookEffect Hook(副作用函数相当于生命周期)Effect Hook(return的函数并不一定等于WillUnMount)useRefuseContextuseReduceruseMemouseCallback额外的hook 自定义hook使用规范注意事项资料

为什么出现hook

上节 我们总结了 react 创建组件的几种方式,我们发现函数组件是不能使用 state 的,state 只能在 class 组件里才有。而

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。

解决的问题

1、组件之间复用状态逻辑的问题 虽然也有 render props 和 高阶组件。但是这类方案需要重新组织你的组件结构,这可能会很麻烦,使你的代码难以理解。 2、复杂组件变得难以理解 例如,组件常常在 componentDidMount 和 componentDidUpdate 中获取数据。但是,同一个 componentDidMount 中可能也包含很多其它的逻辑,如设置事件监听,而之后需在 componentWillUnmount 中清除。相互关联且需要对照修改的代码被进行了拆分,而完全不相关的代码却在同一个方法中组合在一起。如此很容易产生 bug,并且导致逻辑不一致。 Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而并非强制按照生命周期划分。你还可以使用 reducer 来管理组件的内部状态,使其更加可预测。 3、难以理解的 class 对于函数组件与 class 组件的差异也存在分歧,甚至还要区分两种组件的使用场景。class 不能很好的压缩,并且会使热重载出现不稳定的情况。

react 提倡函数式编程,view=fn(props),函数更灵活,更易拆分,更易测试,但函数组件太简单,需要增强能力,所以 Hooks 来了

Hook简介

首先,默认的函数组件是没有 state 的,因为函数组件是一个纯函数,执行完即销毁,无法存储 state,所以需要 State Hook,即把 state 功能“钩”到纯函数中,这样一来不影响函数组件是纯函数,也可以使用state

简单的例子 import React, { useState } from 'react'; function Example() { // 声明一个叫 “count” 的 state 变量。 const [count, setCount] = useState(0); return ( You clicked {count} times // 声明多个 state 变量! const [age, setAge] = useState(42); const [fruit, setFruit] = useState('banana'); const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); // ... } 内置的hook

React 内置了一些像 useState 这样的 Hook。你也可以创建你自己的 Hook 来复用不同组件之间的状态逻辑。

Effect Hook(副作用函数相当于生命周期) 调用时机:执行 DOM 更新之后调用。相当于 componentDidMount 和 componentDidUpdate可以写多个 useEffect 函数 import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // 相当于 componentDidMount 和 componentDidUpdate: useEffect(() => { // 使用浏览器的 API 更新页面标题 document.title = `You clicked ${count} times`; }); return ( You clicked {count} times constructor(props) { super(props); this.state = { count: 0 }; } componentDidMount() { document.title = `You clicked ${this.state.count} times`; } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } render() { return ( You clicked {this.state.count} times count: this.state.count + 1 })}> Click me ); } }

如上,函数组件需要在不同的声明周期写多次,而hook只需要写一次。

副作用函数还可以通过返回一个函数来指定如何“清除”副作用。React 会在组件销毁时取消对 ChatAPI 的订阅

useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; });

举例:

useEffect(() => { let timerId = window.setInterval(() => { console.log(Data.now()) }, 1000) return () => { window.clearInterval(timerId) }; }, []);

通过使用 Hook,你可以把组件内相关的副作用组织在一起(例如创建订阅及取消订阅),而不要把它们拆分到不同的生命周期函数里。

仅在某些变量改变的时候执行:

// class组件的做法 componentDidUpdate(prevProps, prevState) { if (prevState.count !== this.state.count) { document.title = `You clicked ${this.state.count} times`; } } // hook的做法 useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 仅在 count 更改时更新

[count]这里如果是个空数组,那么相当于 componentDidMount 生命周期,只执行一次。这个数组的意思是:谁更新了才执行这个函数。这里的意思就是count 更新了才执行这个函数 []里的东西是控制是否执行componentDidUpdate,而componentDidMount每次都要执行的。

Effect Hook(return的函数并不一定等于WillUnMount) import React, { useState, useEffect } from 'react' function FriendStatus({ friendId }) { const [status, setStatus] = useState(false) // DidMount 和 DidUpdate useEffect(() => { console.log(`开始监听 ${friendId} 在线状态`) // 【特别注意】 // 此处并不完全等同于 WillUnMount // props 发生变化,即更新,也会执行结束监听 // 准确的说:返回的函数,会在下一次 effect 执行之前,被执行 return () => { console.log(`结束监听 ${friendId} 在线状态`) } }) return 好友 {friendId} 在线状态:{status.toString()} } export default FriendStatus import React from 'react' class FriendStatus extends React.Component { constructor(props) { super(props) this.state = { status: false // 默认当前不在线 } } render() { return 好友 {this.props.friendId} 在线状态:{this.state.status} } componentDidMount() { console.log(`开始监听 ${this.props.friendId} 的在线状态`) } componentWillUnMount() { console.log(`结束监听 ${this.props.friendId} 的在线状态`) } // friendId 更新 componentDidUpdate(prevProps) { console.log(`结束监听 ${prevProps.friendId} 在线状态`) console.log(`开始监听 ${this.props.friendId} 在线状态`) } } export default FriendStatus useRef

用来获取dom节点

import React, { useRef, useEffect } from 'react' function UseRef() { const btnRef = useRef(null) // 初始值 useEffect(() => { console.log(btnRef.current) // DOM 节点 }, []) return useContext } from 'react' // 主题颜色 const themes = { light: { foreground: '#000', background: '#eee' }, dark: { foreground: '#fff', background: '#222' } } // 创建 Context const ThemeContext = React.createContext(themes.light) // 初始值 function ThemeButton() { const theme = useContext(ThemeContext) return background: theme.background, color: theme.foreground }}> hello world } function Toolbar() { return } function App() { return useReducer } from 'react' const initialState = { count: 0 } const reducer = (state, action) => { switch (action.type) { case 'increment': return { count: state.count + 1 } case 'decrement': return { count: state.count - 1 } default: return state } } function App() { // 很像 const [count, setCount] = useState(0) const [state, dispatch] = useReducer(reducer, initialState) return count: {state.count} type: 'increment' })}>increment type: 'decrement' })}>decrement } export default App

useReducer和redux的区别?

useReducer是useState的代替方案,用于 state 复杂变化useReducer是单个组件状态管理,组件通讯还需要 propsredux是全局的状态管理,多组件共享数据 useMemo

做性能优化,同 SCU

React默认会更新所有子组件class 组件使用 SCU 和 PureComponent 做优化Hooks 使用useMemo 做优化,原理都是相同的 import React, { useState, memo, useMemo } from 'react' // 子组件 // function Child({ userInfo }) { // console.log('Child render...', userInfo) // return //

This is Child {userInfo.name} {userInfo.age}

// // } // 类似 class PureComponent ,对 props 进行浅层比较 const Child = memo(({ userInfo }) => { console.log('Child render...', userInfo) return This is Child {userInfo.name} {userInfo.age} }) // 父组件 function App() { console.log('Parent render...') const [count, setCount] = useState(0) const [name, setName] = useState('老师') // const userInfo = { name, age: 20 } // 用 useMemo 缓存数据,有依赖 const userInfo = useMemo(() => { return { name, age: 21 } }, [name]) return count is {count} userInfo}> } export default App useCallback

useMemo 缓存数据,useCallback 缓存函数

import React, { useState, memo, useMemo, useCallback } from 'react' // 子组件,memo 相当于 PureComponent const Child = memo(({ userInfo, onChange }) => { console.log('Child render...', userInfo) return This is Child {userInfo.name} {userInfo.age} console.log('Parent render...') const [count, setCount] = useState(0) const [name, setName] = useState('老师') // 用 useMemo 缓存数据 const userInfo = useMemo(() => { return { name, age: 21 } }, [name]) // function onChange(e) { // console.log(e.target.value) // } // 用 useCallback 缓存函数 const onChange = useCallback(e => { console.log(e.target.value) }, []) return count is {count} userInfo} onChange={onChange}> } export default App 额外的hook

可以先不学

自定义hook 必须以 “use” 开头

比如我们刚刚题目开头的例子 function Example() {},这是一个普通的函数组件,只不过这个函数组件里用了 useState 这个 hook,而不是说这个函数组件就是一个 hook,这节我们要自定义一个 hook,是自定义一个像 useState 这样的东西,不要搞混了呦~

// 自定义一个返回用户是否在线的hook function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); // ... return isOnline; } // 使用 function FriendStatus(props) { const isOnline = useFriendStatus(props.friend.id); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; } // 使用 function FriendListItem(props) { const isOnline = useFriendStatus(props.friend.id); return ( color: isOnline ? 'green' : 'black' }}> {props.friend.name} ); }

封装axios自定义hooks

import { useState, useEffect } from 'react' import axios from 'axios' // 封装 axios 发送网络请求的自定义 Hook function useAxios(url) { const [loading, setLoading] = useState(false) const [data, setData] = useState() const [error, setError] = useState() useEffect(() => { // 利用 axios 发送网络请求 setLoading(true) axios.get(url) // 发送一个 get 请求 .then(res => setData(res)) .catch(err => setError(err)) .finally(() => setLoading(false)) }, [url]) return [loading, data, error] } export default useAxios

使用

import React from 'react' import useAxios from '../customHooks/useAxios' function App() { const url = 'http://localhost:3000/' // 数组解构 const [loading, data, error] = useAxios(url) if (loading) return loading... return error ? {JSON.stringify(error)} : {JSON.stringify(data)} } export default App 使用规范

使用 useXxx 命名

只能用于react函数组件,并且在函数里的顶层,不能放 if、for 等里面 在这里插入图片描述

eslint 插件 eslint-plugin-react-hooks

注意事项 useState 初始化值,只有第一次有效 // 这个是子组件,初始化的时候值为userInfo.name,当父组件传入的 userInfo 变化时,并不会修改 name 的值,也就是说 name 只能通过 setName 来修改。 function Child({ userInfo }) { const [name, setName] = useState(userInfo.name) } useState 处理复杂数据类型 const [chartTitleData, setChartTitleData] = useState([ { leftLabel: '共新增', value: '12', rightLabel: '名患者', valueColor: 'rgb(32,128,227)' } ]); setChartTitleData((prevList) => { prevList[0].value = 20; // 注意,这里一定要是不可变值,如果直接返回prevList,引用地址不变,不会更新 return [...prevList]; }); const [chartTitleData, setChartTitleData] = useState({ leftLabel: '共新增', value: '12', rightLabel: '名患者', valueColor: 'rgb(32,128,227)' }); setChartTitleData((prevList) => { prevList.value = 20; // 注意,这里一定要是不可变值,如果直接返回prevList,引用地址不变,不会更新 return {...prevList}; }); useEffect 内部不能修改 state import React, { useState, useRef, useEffect } from 'react' function UseEffectChangeState() { const [count, setCount] = useState(0) // 模拟 DidMount const countRef = useRef(0) useEffect(() => { console.log('useEffect...', count) // 定时任务 const timer = setInterval(() => { console.log('setInterval...', countRef.current) // 这样改变 state 的值会有问题 // setCount(count + 1) // 推荐用 useRef 来解决 setCount(++countRef.current) }, 1000) // 清除定时任务 return () => clearTimeout(timer) }, []) // 依赖为 [] // 依赖为 [] 时: re-render 不会重新执行 effect 函数 // 没有依赖:re-render 会重新执行 effect 函数 return count: {count} } export default UseEffectChangeState useEffect 可能出现死循环 function UseEffectChangeState(config={}) { useEffect(() => { }, [config]) } // 当数组中有引用数据类型的时候,会出现死循环 // 我们复习一下:数组中字段的含义,当这个字段改变时,重新触发这个函数 // 所以如果是引用数据类型,会判断是否相同 Object.is({}, {}),结果还是 false,所以会一直反复的执行 setState 是异步的,具体可看 这篇 const [tabIndex, setTabIndex] = useState(0); useEffect(() => { resetWhichContentList(); getWhichContentList(); }, [tabIndex]); const onChangeTab = async (data) => { setTabIndex(data.i); // 拿不到值 console.log(tabIndex); // 本来在这里执行的逻辑放在 useEffect 中 }; 资料

第三方 Hook https://nikgraf.github.io/react-hooks/ https://github.com/umijs/hooks



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有