React整理总结(七、Hooks)
1.Class组件的优缺点
优点
- class组件可以定义自己的state,用来保存组件自己内部的状态;函数式组件不可以,因为函数每次调用都会产生新的临时变量;
- class组件有自己的生命周期,我们可以在对应的生命周期中完成自己的逻辑;
比如在componentDidMount中发送网络请求,并且该生命周期函数只会执行一次; 函数式组件在学习hooks之前,如果在函数中发送网络请求,意味着每次重新渲染都会重新发送一次网络请求; - class组件可以在状态改变时只会重新执行render函数以及我们希望重新调用的生命周期函数componentDidUpdate等; 函数式组件在重新渲染时,整个函数都会被执行,似乎没有什么地方可以只让它们调用一次;
缺点
- 复杂组件变得难以理解
- 难以理解的class
- 组件复用状态很难:
- 高阶组件;
- redux中connect或者react-router中的withRouter
- 于Provider、Consumer来共享一些状态
hooks
- 简单总结一下hooks:
- 它可以让我们在不编写class的情况下使用state以及其他的React特性;
- 但是我们可以由此延伸出非常多的用法,来让我们前面所提到的问题得到解决;
- Hook的使用场景:
- Hook的出现基本可以代替我们之前所有使用class组件的地方;
- Hook只能在函数组件中使用,不能在类组件,或者函数组件之外的地方使用;
2. 常用hooks
const [msg, setMsg] = useState("hello hooks");
useEffect(() => {
// 执行
return () => {
// 取消
}
}, [//依赖项])
// context.js
import { createContext } from 'react';
const UserContext = createContext();
const ThemeContext = createContext();
export default {
UserContext, ThemeContext
}
// App.jsx
<UserContext.Provider value={{name: 'xiaoming', age: 18}}>
<ThemeContext.Provider value={{color: 'red', size: 12}}>
<App>
</ThemeContext.Provider>
</UserContext.Provider>
// Function Comp
export default () => {
const USER = useContext(UserContext);
const THEME = useContext(ThemeContext);
return (<>
<h2>{USER.name}-{USER.age}</h2>
<h3 style={{color: THEME.color, size: THEME.size}}>content</h3>
</>)
}
// const [state, dispatch] = userReducer(reducer, initState);
import React, { memo, userReducer } from 'react';
function reducer(state, action){
switch(action.type){
case 'add':
return {...state, counter: state.counter + action.payload};
case 'sub':
return {...state, counter: state.counter - action.payload};
default:
return state;
}
}
export default memo(() => {
const [state, dipatch] = useReducer(reducer, {counter: 0});
return <>
<h2>counter: {state.counter}</h2>
<button onClick={() => dispatch({type: 'add', payload: 5})}>+5</button>
<button onClick={() => dispatch({type: 'sub', payload: 5})}>-5</button>
</>
})
useCallback
会返回一个函数的 memoized
(记忆的) 值;在依赖不变的情况下,多次定义的时候,返回的值是相同的;
作为参数传递给子组件时使用
;
const fn = useCallback(() => {
dosomething(a, b);
}, [a, b]);
配合useRef使用
;
const [count, setCount] = useState();
const countRef = useRef();
countRef.current = count;
const addFn = useCallback(() => setCount(countRef.current + 1), []);
useMemo(() ={}, [dep])
对回调函数的结果进行缓存
useCallback(fn,[dep]) = useMemo(() => fn, [dep])
- 进行大量的计算操作,是否有必须要每次渲染时都重新计算;
- 对子组件传递相同内容的对象时,使用useMemo进行性能的优化
-
2.6
useRef
返回一个ref对象,返回的ref对象再组件的整个生命周期保持不变。- 用于获取dom元素
- 用于保存具体的值,解决闭包陷阱
-
2.7
useImperativeHandle
子组件向父组件传递特定的对象
const SonComponent = memo(forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(() => {
return {
focus(){
inputRef.current.focus();
}
setValue(val){
inputRef.current.value = val;
}
} //返回的这个对象会被绑定到ref.current
})
return (<>
<h2>son component<h2>
<input ref={inputRef}/>
</>)
}))
const FatherComponent = memo(() => {
const sonRef = useRef();
return (<>
<h1>father comp</h1>
<SonComponent ref={sonRef} />
</>)
})
-
2.8
useLayoutEffect()
- useEffect会在渲染的内容更新到DOM上后执行,不会阻塞DOM的更新;
- useLayoutEffect会在渲染的内容更新到DOM上之前执行,会阻塞DOM的更新;
3. 自定义hook
- 形如
useFn
,内部可以使用其他hook
// 获取滚动位置
export function useWindomPosition(){
const [position, setPosition] = useState();
const handleScroll = () => {
setPosition(window.scrollY);
}
useEffect(() => {
document.addEventListener('scroll', handleScroll);
return () => {
document.removeEventListener('scroll', handleScroll);
}
})
return position;
}
// 使用localStorage存储数据
export function useLocalStorage(key){
const [data, setData] = useState(() => JSON.parse(window.localStorage.getItem(key)));
useEffect(() => {
window.localStorage.setItem(key, data);
}, [data]);
return [data, setData];
}
4.其他hook
useSelector
将state映射到组件中;useDispatch
直接获取dispatch函数;useStore
获取store对象;- 参数一:将state映射到需要的数据中;
- 参数二:可以进行比较来决定是否组件重新渲染。如果state中的a发生变化,某个组件只用了state中的b,也会重新渲染,所以需要第二个参数优化。
// 1.获取state
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
const { count } = useSelector((state) => {
return {
count: state.counter.count
}
},shallowEqual)
useId
用于生成横跨服务端和客户端的稳定的唯一 ID 的同时避免 hydration 不匹配的 hook。- useId是用于react的同构应用开发的,前端的SPA页面并不需要使用它;
- useId可以保证应用程序在客户端和服务器端生成唯一的ID,这样可以有效的避免通过一些手段生成的id不一致,造成
hydration mismatch;
useTransition
返回一个状态值表示过渡任务的等待状态,以及一个启动该过渡任务的函数。告诉react对于某部分任务的更新优先级较低,可以稍后进行更新。
useDeferredValue
接受一个值,并返回该值的新副本,该副本将推迟到更紧急地更新之后。