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);
}
两种写法的区别:
-
使用第一种写法
当进行多次
setCount
时,如:setCounnt(count + 1); setCounnt(count + 1); setCounnt(count + 1);
最终
count
的值为1,因为setCount
会进行合并,相当于只加了一次,和setState
一样。 -
使用第二种写法时
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 不进行依赖限制
组件第一次加载就会执行和每次组件更新也会执行。
类似集成了componentDidMount
和componentDidUpdate
。
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组件不重新渲染。
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]);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步