一、useCallBack的作用以及使用
作用:返回一个被缓存的函数。 配合React.memo使用。
使用:
// 新建父组件
import { useState, useCallback } from 'react';
import Children from './children';
function App() {
const [count, setCount] = useState(0);
const [rely, setRely] = useState('aaa');
// 不使用useCallback
const increment = () => {
setCount((count) => {
return count + 1;
})
}
// 通过useCallback包装
const increment = useCallback(() => {
setCount((count) => {
return count + 1;
})
}, [rely])
const change = () => {
setRely('bbb')
}
return (
<div className="App">
count: {count}
<button onClick={change}>父组件改变依赖</button>
<Children increment={increment} />
</div>
);
}
export default App;
// 新建子组件
import React, { memo } from 'react';
const Children = memo((props) => {
const { increment } = props;
console.log('渲染111111')
return (
<div>
<button onClick={increment}>子组件count+1</button>
</div>
)
})
export default Children;
通过点击子组件按钮可见子组件每次都会渲染。
使用useCallback后,点击子组件按钮,子组件不会重复渲染,只在初始化的时候渲染一次。
点击按钮改变依赖值,子组件才会被重新渲染
二、useMemo的作用以及使用
作用:返回一个被缓存的值
使用:
import { useState, useMemo } from 'react';
import './App.css';
function App() {
const [count, setCount] = useState(0);
const [total, setTotal] = useState(0);
// 没有使用 useMemo,即使是更新 total, countToString 也会重新计算
const countToString = (() => {
console.log("countToString 被调用");
return count.toString();
})();
// 使用了 useMemo, 只有 total 改变,才会重新计算
const totalToStringByMemo = useMemo(() => {
console.log("totalToStringByMemo 被调用");
return total + "";
}, [total]);
return (
<div className="App">
<h3>countToString: {countToString}</h3>
<h3>countToString: {totalToStringByMemo}</h3>
<button
onClick={() => {
setCount((count) => count + 1);
}}
>
Add Count
</button>
<br />
<button
onClick={() => {
setTotal((total) => total + 1);
}}
>
Add Total
</button>
</div>
);
}
export default App;
分别点击按钮观察
可以发现使用了useMemo缓存的值不会重新计算
三、useReducer的作用以及使用
作用:useState的一种替代品,在一些state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等,可以使用useReducer。它接收当前应用的state和触发的动作action,计算并返回最新的state。
参数:接收三个参数,分别是:1.定义的reducer函数,用于更新state。2.state的初始值。2.第三个定义的init函数,如果加了这个参数,即对定义的初始值执行init函数,这么做可以将用于计算 state 的逻辑提取到 reducer 外部,这也为将来对重置 state 的 action 做处理提供了便利。
使用:
我们简单写一个登录来感受下useState和useReducer。
使用useState写登录
import React, { useState } from "react";
function Example() {
const [name, setName] = useState(""); // 用户名
const [pwd, setPwd] = useState(""); // 密码
const [isLoading, setIsLoading] = useState(false); // 是否展示loading,发送请求中
const [error, setError] = useState(""); // 错误信息
const [isLoggedIn, setIsLoggedIn] = useState(false); // 是否登录
const login = (name, pwd) => {
setIsLoading(true);
if (name === "123" && pwd === "123") {
setError("");
setIsLoggedIn(true);
setIsLoading(false);
} else {
setError("error");
setName("");
setPwd("");
setIsLoggedIn(false);
setIsLoading(false);
}
};
return (
<div>
用户名:
<input
type="text"
onChange={(e) => setName(e.target.value)}
value={name}
/>
<br />
密码:
<input type="text" onChange={(e) => setPwd(e.target.value)} value={pwd} />
<br />
<button onClick={() => login(name, pwd)} disabled={isLoading}>
登录
</button>
<br />
{isLoggedIn ? "登录成功" : error}
</div>
);
}
我们定义了5个state来描述页面的状态,在login函数中当登录成功、失败时进行了一系列复杂的state设置。可以想象随着需求越来越复杂更多的state加入到页面,更多的setState分散在各处,很容易设置错误或者遗漏,维护这样的老代码更是一个噩梦。
使用useReducer写登录
import React, { useReducer } from "react";
const initState = {
name: "",
pwd: "",
isLoading: false,
error: "",
isLoggedIn: false,
};
function loginReducer(state, action) {
switch (action.type) {
case "name":
return {
...state,
name: action.payload.name,
};
case "pwd":
return {
...state,
pwd: action.payload.pwd,
};
case "login":
return {
...state,
isLoading: true,
error: "",
};
case "success":
return {
...state,
isLoggedIn: true,
isLoading: false,
error: "",
};
case "error":
return {
...state,
error: action.payload.error,
name: "",
pwd: "",
isLoading: false,
isLoggedIn: false,
};
default:
return state;
}
}
function Example() {
const [state, dispatch] = useReducer(loginReducer, initState);
const { name, pwd, isLoading, error, isLoggedIn } = state;
const login = (name, pwd) => {
dispatch({ type: "login" });
if (name === "123" && pwd === "123") {
dispatch({ type: "success" });
} else {
dispatch({
type: "error",
payload: { error: "登录失败" },
});
}
};
return (
<div>
用户名:
<input
type="text"
onChange={(e) => {
dispatch({
type: "name",
payload: { name: e.target.value },
});
}}
value={name}
/>
<br />
密码:
<input
type="text"
onChange={(e) => {
dispatch({
type: "pwd",
payload: { pwd: e.target.value },
});
}}
value={pwd}
/>
<br />
<button onClick={() => login(name, pwd)} disabled={isLoading}>
登录
</button>
<br />
{isLoggedIn ? "登录成功" : error}
</div>
);
}
四、useRef的作用以及使用
1.对useRef的理解
- 返回一个可变的 ref 对象,该对象只有个 current 属性,初始值为传入的参数( initialValue )。
- 返回的 ref 对象在组件的整个生命周期内保持不变
- 当更新 current 值时并不会 re-render ,这是与 useState 不同的地方
- 更新 useRef 是 side effect (副作用),所以一般写在 useEffect 或 event handler 里
- useRef 类似于类组件的 this
案例1.获取组件内的dom值
import React, { useRef } from 'react'
const App = () => {
const inputEl = useRef(null)
const handleFocus = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus()
}
return (
<p>
<input ref={inputEl} type="text" />
<button onClick={handleFocus}>Focus the input</button>
</p>
)
}
export default App
案例2.采用alert弹出实时的值
不采用useRef
import { useState, useMemo } from 'react';
import './App.css';
function App() {
const [like, setLike] = useState(0)
console.log('11111')
function handleAlertClick() {
setTimeout(() => {
alert(`you clicked on ${like}`)
}, 2000)
}
return (
<>
<button onClick={() => setLike(like + 1)}>{like}赞</button>
<button onClick={handleAlertClick}>Alert</button>
</>
)
}
export default App;
当我们点击使like + 1,当点击到4时,点击alert,然后继续点击使得like + 1
这样子并不能拿到当时的值。
使用useRef
import { useState, useRef, useEffect } from 'react';
import './App.css';
function App() {
// 定义一个实例变量
let like = useRef(0);
const [stat, setStat] = useState(like.current);
function handleAlertClick() {
setTimeout(() => {
alert(`you clicked on ${like.current}`);
}, 2000);
}
useEffect(() => {
console.log(like.current, 'like.current');
}, [like])
return (
<>
<button
onClick={() => {
like.current = like.current + 1;
setStat(like.current)
}}
>
{stat}
</button>
<button onClick={handleAlertClick}>Alert</button>
</>
);
}
export default App;
执行上面同样的操作
可以看到可以实时拿到值。
前端小白,感谢关注😊