此文仅为官网部分材料整理供个人使用,大家直接看 React Hook 官网 介绍,不要看这里。
为什么要搞 Hooks
- 在组件之间复用状态逻辑很难
- 复杂组件变得难以理解(在生命周期函数里做各种事情)
- 难以理解的 class
使用 useState
如何使用
[状态变量, 更改函数] = useState(初始值)
Js中文网 – 前端进阶资源教程 www.javascriptC.com,typescript 中文手册
专注分享前端知识,你想要的,在这里都能找到
如何记录值
- 根据 useState 出现的顺序来确定的 值的 “位置”
- 即每一次
[状态变量, 更改函数] = useState(初始值)
调用,都在闭包内创建一个空间,以顺序做 index 提取 - 基于第二点,useState 必须写在函数最外层,避免嵌套在 if / else 逻辑内
使用 useEffect
注意
useEffect 运行时机类同于 componentDidMount、componentDidUpdate、componentWillUnmount 这三个生命周期函数,执行 Dom 更新后,才会调用
如何使用
useEffect(() => {
// do something
document.title = `You clicked ${count} times`;
})
如何清除
useEffect(() => {
// do something
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// 通过返回一个回调函数的方式来注销
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
}
})
跳过一些不必要的副作用函数
useEffect(() => {
document.title = `You clicked ${count} times`;
// 只有当count的值发生变化时,才会重新执行`document.title`这一句
// 不要传递空数组,可能会有其他问题
}, [count]);
规则
- 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用
- 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。(还有一个地方可以调用 Hook —— 就是自定义的 Hook 中,我们稍后会学习到。)
每次 State Hook 函数调用,都是全新的一个 state。 每次 Effect Hook 调用,都是重新的一个新函数执行,每个 effect “属于”一次特定的渲染。
自定义 Effect Hooks
Js中文网 – 前端进阶资源教程 www.javascriptC.com,typescript 中文手册
专注分享前端知识,你想要的,在这里都能找到
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
Hook API 索引
基础 Hook
useState
略
useEffect
略
useContext
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
// useContext(MyContext) 相当于 class 组件中的 static contextType = MyContext 或者 <MyContext.Consumer>
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
额外的 Hook
useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
useCallback
当把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用
useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
// a, b 改变时,才会执行 doSomething
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
你可以把 useMemo 作为性能优化的手段,但不要把它当成语义上的保证。将来,React 可能会选择“遗忘”以前的一些 memoized 值,并在下次渲染时重新计算它们,比如为离屏组件释放内存。先编写在没有 useMemo 的情况下也可以执行的代码 —— 之后再在你的代码中添加 useMemo,以达到优化性能的目的。
简言: 先不要用,以后有需要再用
useRef
const refContainer = useRef(initialValue);
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
useImperativeHandle
可以让你在使用 ref 时自定义暴露给父组件的实例值,个人感觉:这个基本上应该用不太到
useImperativeHandle(ref, createHandle, [deps])
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
useLayoutEffect
其函数签名与 useEffect 相同,但它会在 所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
尽可能使用标准的 useEffect 以避免阻塞视觉更新。
推荐你一开始先用 useEffect,只有当它出问题的时候再尝试使用 useLayoutEffect
useDebugValue
用于在 React 开发者工具中显示 自定义 hook 的标签
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// ...
// 在开发者工具中的这个 Hook 旁边显示标签
// e.g. "FriendStatus: Online"
useDebugValue(isOnline ? 'Online' : 'Offline');
return isOnline;
}
FAQ
我该如何使用 Hook 进行数据获取?
请求的 fetch 放在 useEffect 的回调函数内
如何获取上一轮的 props 或 state?
function Counter() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return <h1>Now: {count}, before: {prevCount}</h1>;
}
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
该如何测量 DOM 节点?
你可以使用 callback ref
function MeasureExample() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height);
}
}, []);
return (
<>
<h1 ref={measuredRef}>Hello, world</h1>
<h2>The above header is {Math.round(height)}px tall</h2>
</>
);
}
在这个案例中,我们没有选择使用 useRef,因为当 ref 是一个对象时它并不会把当前 ref 的值的 变化 通知到我们。使用 callback ref 可以确保 即便子组件延迟显示被测量的节点 (比如为了响应一次点击),我们依然能够在父组件接收到相关的信息,以便更新测量结果。
注意到我们传递了 [] 作为 useCallback 的依赖列表。这确保了 ref callback 不会在再次渲染时改变,因此 React 不会在非必要的时候调用它。
抽象封装
function useClientRect() {
const [rect, setRect] = useState(null);
const ref = useCallback(node => {
if (node !== null) {
setRect(node.getBoundingClientRect());
}
}, []);
return [rect, ref];
}
作者:Xaber
链接:https://juejin.im/post/5df1e4aae51d45580a4acfc4
看完两件小事
如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:
- 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
- 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程
本文著作权归作者所有,如若转载,请注明出处
转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com