目录
跳转链接 => React_01 学习笔记
跳转链接 => React_02 学习笔记
跳转链接 => React_03 学习笔记
跳转链接 => React_04 菜谱项目
目标
- 掌握 hook 相关函数方法
- 能够自定义 hook 函数
一、hook
1.1、简介
在 React 的世界中,有容器组件 和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用 函数,无状态组件来展示 UI,而对于 容器组件,函数组件 就显得无能为力,我们依赖于 类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行 渲染。React在 v16.8 的版本中推出了 React Hooks 新特性,Hook 是 一套工具 函数 的集合,它增强了 函数组件 的功能,hook 不等于 函数组件,所有的 hook 函数 都是以 use开头 。
使用 React Hooks 相比于从前的 类组件 有以下几点好处:
- 代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的 生命周期函数 中,容易使开发者不利于维护 和 迭代,通过 React Hooks 可以将功能代码 聚合,方便阅读 维护
- 组件树 层级变浅,在原本的代码中,我们经常使用 HOC / render / Props 等方式来 复用组件的状态,增强功能等,无疑增加了 组件树层数 及 渲染,而在 React Hooks 中,这些功能都可以通过强大的 自定义 的 Hooks 来实现
1.2、使用 hook 限制
- hook 只能用在 函数组件 中,class 类组件不行
- 普通函数 不能使用 hook -- 例外:普通函数 名称以 use 开头也可以
- 函数组件 内部的函数 也不行
- hook 函数一定要放在 函数组件 的 第一层,别放在 if / for 中( 块级作用域 )
- 要求 函数组件 名称必须 首字母 大写
1.3、常用 hook 函数
1.3.1、useState
保存 组件状态,功能类似于 类组件 中的 this.state 状态管理
1.3.2、useEffect
此 hook 可以 模拟 函数组件 的 生命周期,函数组件 对于在一些 生命周期 中操作还是无能为力,所以 React 提供了 useEffect 来帮助开发者处理 函数组件,来帮助模拟完成一部份的开发中非常常用的 生命周期 方法。常被别的称为:副作用处理函数。此函数的操作是 异步 的。
// useEffect 相当类组件中的 3 个生命周期
// componentDidMount componentDidUpdate componetWillUnMount
import React, { useState, useEffect } from 'react'
const App = () => {
let [count, setCount] = useState(0)
// 参数 1:回调函数
// 如果没有第 2个参数,表示 componentDidMount componentDidUpdate
// 如果第 2个参数 为空数组 表示 componentDidMount
// 如果第 2个参数 不为空数组,只关注所写变量事件更新和挂载
// 表示 componentDidMount componentDidUpdate
useEffect(() => {
if (count > 10) {
document.title = count
}
// 返回 回调函数中就是 componetWillUnMount
// 在执行下一个 effect 之前,上一个 effect 就已被清除
return () => {
// 在此处,就可以把 setTimout setInterval 清空
}
}, [count]);
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>+++</button>
</div>
);
}
import React, { useEffect, useState } from 'react';
// 副作用函数,它可以模拟 类组件中的 3 个生命周期
// componentDidMount componentDidUpdate componentWillUnMount
const Child = () => {
let [count, setCount] = useState(100)
let [name, setName] = useState('')
let [user, setUser] = useState({ id: 1, name: { age: 20 } })
// 此函数的参数有 2 个
// 第 1 个参数为 回调函数据,此回调函数中不能使用 async / await
// 第 2 个参数为一个 数组,可选
//-----------------------------------------------------
// 如果只有参数 1,且参数 1 回调中没有返回一个函数则模拟的为
// componentDidMount componentDidUpdate
// 在没有第 2 个参数时,一般不建议进行 网络请求 或 更改数据,有可能出现死循环
/* useEffect(() => {
console.log('didmount和update');
}) */
//-----------------------------------------------------
// 如果参数 2 为一个 空数据,则模拟 componentDidMount
/* useEffect(() => {
console.log('didmount');
}, []) */
//-----------------------------------------------------
// 如果参数 2 不是一个 空数据
// 则监听数组中对象的变量数据变化 componentDidMount componentDidUpdate
/* useEffect(() => {
console.log('持载和count发生更新了');
}, [count, user]) */
//-----------------------------------------------------
// 参数1中返回一个函数,此函数就是一个模拟销毁的生命周期 componentWillUnMount
useEffect(() => {
console.log('持载和count发生更新了');
// 数据更新时会触发 和 组件被销毁时都会触发
return () => {
console.log('销毁');
}
}, [count])
console.log('render');
return (
<div>
<h3>Child</h3>
<h3>count:{count} --- <button onClick={() => setCount(count + 1)}>++++</button></h3>
<div>
<input type="text" value={name} onChange={el => setName(el.target.value)} />
</div>
<hr />
<h3 onClick={() => {
/* user.name.age = 30
setUser(user) */
setUser(user => ({ ...user, name: { ...user.name, age: 30 } }))
}}>{user.name.age}</h3>
</div >
)
}
const App = () => {
let [show, setShow] = useState(true)
return (
<div>
{
show ? <Child /> : <div>abc</div>
}
</div>
);
}
export default App;
1.3.3、useReducer
useReducer 这个 Hooks 在使用上几乎跟 Redux 一模一样,唯一缺少的就是无法使用 redux 提供的 中间件。
import React, { useState, useReducer } from 'react'
const initialState = { count: 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();
}
}
const App = () => {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<div>
<h3>{state.count}</h3>
<button onClick={() => dispatch({ type: 'increment' })}>+++</button>
<button>---</button>
</div>
);
}
1.3.4、useContext
使用 useContext 可以方便我们获取 Context 中的数据源
import React, { useContext } from 'react'
import cxt from '../context/userContext'
const { Provider } = cxt
const App = () => {
return (
<div>
<Provider value='hello'>
<Cmp />
</Provider>
</div>
)
}
function Cmp() {
const context = useContext(cxt)
return (
<h1>
{context}
</h1>
)
}
1.3.5、useMemo
记忆组件,可以理解为 计算属性 性能优化
import React, { useState, useMemo } from 'react';
const Memo = () => {
const [count, setCount] = useState(1)
const [val, setValue] = useState('')
// 返回上一次缓存的值
const sum = useMemo(() => {
return count + 10
}, [count]);
return (
<div>
<h3>{count}-{val}-{sum}</h3>
<div>
<button onClick={() => setCount(count + 1)}>+ count</button>
<input value={val} onChange={event => setValue(event.target.value)} />
</div>
</div>
)
}
1.3.6、useCallback
记忆函数,它计算返回一个 缓存函数。
import React, { useState, useCallback, useEffect } from 'react';
const App = () => {
const [count, setCount] = useState(1);
const [val, setVal] = useState('');
const callback = useCallback(() => {
return count
}, [count])
return (
<div>
<h1>父组件:{count}</h1>
<Child callback={callback} />
<div>
<button onClick={() => setCount(count + 1)}>+count</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>
<hr />
<div>子组件:{count}</div>
</div>
)
}
React.memo
给函数组件来减少重复渲染的 顶层Api ,类似于 PureComponent
1.3.7、useRef
useRef 跟 createRef 类似,都可以用来生成对 DOM 对象的引用
useRef 默认只能在 html 标签中使用,不可以在 函数组件 中使用
import React, { useRef } from 'react'
const Ref = () => {
const username = useRef()
const login = () => {
console.log(username.current.value);
}
return (
<div>
<div>
<input type="text" ref={username} />
<br />
<br />
<button onClick={login}>添加用户</button>
</div>
</div>
)
}
1.3.8、useImperativeHandle
使用它可以透传 Ref ,因为 函数组件 没有 实例,所以在默认 自定义函数组件 中不能使用 ref 属性,使用为了解决此问题,react 提供了一个 hook 和一个 高阶组件 来帮助 函数组件 能够使用 ref 属性。
import React, { useRef, useEffect, useImperativeHandle, forwardRef } from "react"
function ChildInputComponent(props, useRef) {
// const inputRef = useRef(null)
// useImperativeHandle(ref, () => inputRef.current)
// 穿透 ref
useImperativeHandle(ref, () => ({id:1,name:’aaa’}))
return <input type="text" ref={ useRef } />
}
const ChildInput = forwardRef(ChildInputComponent)
function App() {
const inputRef = useRef(null)
const getValue = () => {
console.log(inputRef.current.value)
}
return (
<div>
<ChildInput ref={inputRef} />
<hr/>
<button onClick={getValue}>获取一下数据</button>
</div>
)
}
1.3.9、useLayoutEffect
大部分情况下,使用 useEffect 就可以帮我们处理组件的 副作用,但是如果想要 同步调用一些 副作用,比如对 DOM 的操作,就需要使用 useLayoutEffect ,useLayoutEffect 中的 副作用 会在 DOM 更新之后 同步执行。
const Layout = () => {
const [value, setValue] = useState(0)
useLayoutEffect(() => {
const title = document.querySelector("#title")
console.log("useLayoutEffect");
setValue(title.innerHTML);
})
useEffect(() => {
console.log("useEffect")
})
return (
<div>
<div><h1 id="title">hello</h1><h2>{value}</h2></div>
</div>
)
}
1.3.10、react-redux-hook
react-redux 支持了 hook 的解决方案,提供两个方法,很方便我们在项目去使用
import React from 'react'
// useDispatch 发送 指令
// useSelector 获取 数据
import { useDispatch, useSelector } from 'react-redux'
const MyRedux = () => {
const { username, password } = useSelector(state => state.userinfo)
const dispatch = useDispatch()
return (
<div>
<h3>{username}</h3>
<hr />
<button onClick={
e => {
dispatch({
type: 'setname',
name: 'aaaaa'
})
}
}>修改一下姓名</button>
</div>
)
}
1.3.11、react-router-dom-hook
react-router-dom 也提供了 hook 的 解决方案
import {useHistory,useLocation,useParams} from 'react-router-dom'
1.4、自定义 hook
定义自定义 hook 以 use 开头 + 函数名称,通过自定义 Hook ,可以将 组件 逻辑提取到 可重用 的函数 中。
import React, {useState, useEffect} from 'react'
// 是否在线
function useOnline() {
const [online, setOnline] = useState(navigator.onLine)
useEffect(() => {
const handlerOnline = () => setOnline(true)
const handlerOffline = () => setOnline(false)
window.addEventListener('online', handlerOnline, false)
window.addEventListener('offline', handlerOffline, false)
return () => {
window.removeEventListener('online', handlerOnline, false)
window.removeEventListener('offline', handlerOffline, false)
}
})
return online
}
function App() {
const online = useOnline()
return (
<div>
{
online ?
<h3 style={{color: 'green'}}>在线</h3>
:
<h3>离线了 </h3>
}
</div>
)
}
图例 :
跳转链接 => React_01 学习笔记
跳转链接 => React_02 学习笔记
跳转链接 => React_03 学习笔记
跳转链接 => React_04 菜谱项目