以下是上一代标准写法类组件的缺点,也正是hook要解决的问题
设计目的
Function Component中每次Render都会形成一个快照并保留下来,这样就确保了状态可控,hook默认每次都更新,会导致重复请求等一系列问题,如果给[]就会一尘不变,因此用好hooks最重要就是学会控制它的变化
在大部分情况下我们只要遵循 React 的默认行为,因为 React 只更新改变了的 DOM 节点,不过重新渲染仍然花费了一些时间,除非它已经慢到让人注意了
基于上面的两点,我们通常的解决方案是:
传统上认为,在 React 中使用内联函数对性能的影响,与每次渲染都传递新的回调会如何破坏子组件的 shouldComponentUpdate 优化有关, 使用useCallback缓存函数引用,再传递给经过优化的并使用引用相等性去避免非必要渲染的子组件时,它将非常有用
const Button = React.memo((props) => {
// 你的组件
}, fn);// 也可以自定义比较函数
function Table(props) {
// ⚠️ createRows() 每次渲染都会被调用
const [rows, setRows] = useState(createRows(props.count));
// ...
// ✅ createRows() 只会被调用一次
const [rows, setRows] = useState(() => createRows(props.count));
// ...
}
function Image(props) {
// ⚠️ IntersectionObserver 在每次渲染都会被创建
const ref = useRef(new IntersectionObserver(onIntersect));
// ...
}
function Image(props) {
const ref = useRef(null);
// ✅ IntersectionObserver 只会被惰性创建一次
function getObserver() {
if (ref.current === null) {
ref.current = new IntersectionObserver(onIntersect);
}
return ref.current;
}
// 当你需要时,调用 getObserver()
// ...
}
useEffect(function persistForm() {
if (name !== '') {
localStorage.setItem('formData', name);
}
});
useEffect(() => {
document.title = "Hello, " + name;
}, [name]); // 以useEffect为示例,适用于所有hook
直到 name 改变时的 Rerender,useEffect 才会再次执行,保证了性能且状态可控
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, [count]);// 以useEffect为示例,适用于所有hook
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
实际应用场景往往不是一个hook能搞定的,长篇大论未必说的清楚,直接上例子(来源于官网摘抄,网络收集,自我总结)
【将更新与动作解耦】-【useEffect,useReducer,useState】
该函数将接收先前的 state,并返回一个更新后的值
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(id);
}, []);
import React, { useReducer, useEffect } from "react";
const initialState = {
count: 0,
step: 1,
};
function reducer(state, action) {
const { count, step } = state;
if (action.type === 'tick') {
return { count: count + step, step };
} else if (action.type === 'step') {
return { count, step: action.step };
} else {
throw new Error();
}
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
const { count, step } = state;
console.log(count);
useEffect(() => {
const id = setInterval(() => {
dispatch({ type: 'tick' });
}, 1000);
return () => clearInterval(id);
}, [dispatch]);
return (
<>
<h1>{count}</h1>
<input value={step} onChange={e => {
dispatch({
type: 'step',
step: Number(e.target.value)
});
}} />
</>
);
}
【通过 context 往下传一个 dispatch 函数】-【createContext,useReducer,useContext】
/**index.js**/
import React, { useReducer } from "react";
import Count from './Count'
export const StoreDispatch = React.createContext(null);
const initialState = {
count: 0,
step: 1,
};
function reducer(state, action) {
const { count, step } = state;
switch (action.type) {
case 'tick':
return { count: count + step, step };
case 'step':
return { count, step: action.step };
default:
throw new Error();
}
}
export default function Counter() {
// 提示:`dispatch` 不会在重新渲染之间变化
const [state, dispatch] = useReducer(reducer, initialState);
return (
<StoreDispatch.Provider value={dispatch}>
<Count state={state} />
</StoreDispatch.Provider>
);
}
/**Count.js**/
import React, { useEffect,useContext } from 'react';
import {StoreDispatch} from '../index'
import styles from './index.css';
export default function(props) {
const { count, step } = props.state;
const dispatch = useContext(StoreDispatch);
useEffect(() => {
const id = setInterval(() => {
dispatch({ type: 'tick' });
}, 1000);
return () => clearInterval(id);
}, [dispatch]);
return (
<div className={styles.normal}>
<h1>{count}</h1>
<input value={step} onChange={e => {
dispatch({
type: 'step',
step: Number(e.target.value)
});
}} />
</div>
);
}
【层层依赖,各自管理】-【useEffect,useCallback,useContext】
function App() {
const [count, setCount] = useState(1);
const countRef = useRef();// 在组件生命周期内保持唯一实例,可穿透闭包传值
useEffect(() => {
countRef.current = count; // 将 count 写入到 ref
});
// 只有countRef变化时,才会重新创建函数
const callback = useCallback(() => {
const currentCount = countRef.current //保持最新的值
console.log(currentCount);
}, [countRef]);
return (
<Parent callback={callback} count={count}/>
)
}
function Parent({ count, callback }) {
// count变化才会重新渲染
const child1 = useMemo(() => <Child1 count={count} />, [count]);
// callback变化才会重新渲染,count变化不会 Rerender
const child2 = useMemo(() => <Child2 callback={callback} />, [callback]);
return (
<>
{child1}
{child2}
</>
)
}
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
function useUpdate(fn) {
const mounting = useRef(true);
useEffect(() => {
if (mounting.current) {
mounting.current = false;
} else {
fn();
}
});
}
function useIsMounted(fn) {
const [isMount, setIsMount] = useState(false);
useEffect(() => {
if (!isMount) {
setIsMount(true);
}
return () => setIsMount(false);
}, []);
return isMount;
}
function useInertRef(obj) { // 传入一个实例 new IntersectionObserver(onIntersect)
const ref = useRef(null);
if (ref.current === null) {
// ✅ IntersectionObserver 只会被惰性创建一次
ref.current = obj;
}
return ref.current;
}
近日,据 MIT Technology Review 报道,一位名为“Repairnator”的机器人在 GitHub 上“卧底”数月,查找错误并编写和提交修复补丁,结果有多个补丁成功通过并被采纳,这位 Repairnator 到底是如何拯救程序员于水火的呢?
你还在为该使用无状态组件(Function)还是有状态组件(Class)而烦恼吗?你还在为搞不清使用哪个生命周期钩子函数而日夜难眠吗?你在还在为组件中的this指向而晕头转向吗?这样看来,说React Hooks是今年最劲爆的新特性真的毫不夸张。
我们将userReducer函数返回的原始dispath命名为origin_dispatch,自定义dispatch函数,当action为函数的时候,我们执行action函数,并将origin_dispatch当作参数传进去;action不是函数,直接调用origin_dispatch,不做处理
使用useEffect 就像瑞士军刀。它可以用于很多事情,从设置订阅到创建和清理计时器,再到更改ref的值。与 componentDidMount、componentDidUpdate 不同的是,在浏览器完成布局与绘制之后,传给 useEffect 的函数会延迟调用。
从 React Hooks 正式发布到现在,我一直在项目使用它。但是,在使用 Hooks 的过程中,我也进入了一些误区,导致写出来的代码隐藏 bug 并且难以维护。这篇文章中,我会具体分析这些问题,并总结一些好的实践,以供大家参考
Hooks 的 API 可以参照 React 官网。本文主要是结合 Demo 详细讲解如何用 Hooks 来实现 React Class Component 写法,让大家更深的理解 Hooks 的机制并且更快的入门。 注意:Rax 的写法和 React 是一致的
Hooks出来已经有段时间了,相信大家都用过段时间了,有没有小伙伴们遇到坑呢,我这边就有个 setInterval 的坑,和小伙伴们分享下解决方案。写个 count 每秒自增的定时器,如下写法结果,界面上 count 为 1 ?
对于 React 16.7 中新的 hooks 系统在社区中引起的骚动,我们都有所耳闻了。人们纷纷动手尝试,并为之兴奋不已。一想到 hooks 时它们似乎是某种魔法,React 以某种甚至不用暴露其实例
9月份开始,使用了React16.8的新特性React Hooks对项目进行了重构,果然,感觉没有被辜负,就像阮一峰老师所说的一样,这个 API 是 React 的未来。
由于篇幅所限文章中并没有给出demo的所有代码,大家如果有兴趣可以将代码clone到本地从commit来看整个demo的TDD过程,配合文章来看会比较清晰,从进公司前认识了TDD,到实践TDD
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!