1.useState
const [ value , setValue] = useState(0)
↓ ↓ ↓
this.state.value this.setState() 初始值
2.useEffect
useEffect(()=>{
拥有componentDidMount,componentWillUpdate生命周期的功能
return ()=>{
拥有componentWillUnmount(卸载
}
},[一个字段,更新再执行])
- 可以写多个useEffect
- useEffect 里面使用到的state的值, 固定在了useEffect内部, 不会被改变,除非useEffect刷新,重新固定state的值
- useEffect不能被判断包裹
- useEffect不能被打断
3.useReducer
- 类似redux中的reducer
字段 入口函数 判断的函数
↑ ↑ ↑
const [count, dispatch] = useReducer((state = 0, {type})=>{
switch (type) {
case "add":
return state+1
case 'delete':
return state-1
default:
return state;
}
}, 0) ← 初始值
<Button onClick={()=> dispatch({type:'add'})}></Button>
4.useContext
const TestContext = React.createContext()
function Parent() {
return <TestContext.Provider value={'test context'}></TestContext.Provider>
}
function Child() {
return <TestContext.Consumer>
value => <div>I am {value}}</div>
</TestContext.Consumer>
}
function App() {
return (
<Parent>
<Child />
</Parent>)
}
5.useRef
- 可以用来获取组件实例对象或者是DOM对象
Dom对象
- 代码中用useRef创建了couterRef对象,并将其赋给了 标签元素 的ref属性。这样,通过访问couterRef.current就可以访问到 标签元素 对应的DOM对象。
“跨渲染周期”保存数据
- 可以使用useRef来跨越渲染周期存储数据,而且对它修改也不会引起组件渲染
const refContainer = useRef(initialValue);
6.Memo
- 类似 PureComponent 不用再写 shouldComponentUpdate 生命周期函数判断参数有无变化来控制组件的渲染。
- 但是这是用来判断prevProps和nextProps是否相等,相等返回true,组件不发生重新渲染;反之,组件重新渲染。主要用于性能优化,不要过于依赖,容易发生bug
const areEqual = (prevProps, nextProps) => {
return prevProps.site_info === nextProps.site_info
}
const data = memo(()=>{
return {
...
}
},[areEqual])
7.useCallback
- 有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。
const fnA = useCallback(fnB, [a])
7.useMemo
- 简单来说就是传递一个创建函数和依赖项,创建函数会需要返回一个值,只有在依赖项发生改变的时候,才会重新调用此函数,返回一个新的值。
onst [count, setCount] = useState(0);
const userInfo = useMemo(() => {
return {
// ...
name: "Jace",
age: count
};
}, [count]);
return <UserCard userInfo={userInfo}>
注: useEffect、useMemo、useCallback都是自带闭包的。也就是说,每一次组件的渲染,其都会捕获当前组件函数上下文中的状态(state, props),所以每一次这三种hooks的执行,反映的也都是当前的状态,你无法使用它们来捕获上一次的状态。对于这种情况,我们应该使用ref来访问。
为什么用hook
业务变得复杂之后,组件之间共享状态变得频繁,此时组件将变得非常难以理解和维护,复用状态逻辑更是难上加难。也许你会说,我们可以引入redux来管理状态呀,是的,我在之前的工作中使用redux已是常态,配合redux-thunk或者saga还可以解决异步redux的问题,但是问题是,这些的使用将极大的加大项目的复杂度和体积,而且需要按照一套严格的标准去写组件和redux,才能保证一定的可维护和可扩展性。
首先,class的方式不能很好的的打包工具压缩,并且需要编译大量的冗余代码,这是我们不希望的。其次根据以往的经验,大量的class会使热重载出现不稳定的情况。所以我们更希望使用一个使代码更易于优化的 API。
这个问题是redux社区公认的问题。所以,基于这几个痛点,我们来展开接下来的学习。
总结:
-
Hook是对函数式组件的一次增强,使得函数式组件可以做到class组件的state和生命周期。
-
Hook的语法更加简练易懂,消除了class的生命周期方法导致的重复逻辑代码,解决了高阶组件难以理解和使用困难的问题。
-
然而Hook并没有让函数式组件能做到class组件做不到的事情,它只是让很多事情变得更加简单而已。
-
class组件并不会消失,但hook化的函数式组件将是趋势。
//防抖
import { useEffect, useRef } from 'react'
const useDebounce = (fn, ms = 30, deps = []) => {
let timeout = useRef()
useEffect(() => {
if (timeout.current) clearTimeout(timeout.current)
timeout.current = setTimeout(() => {
fn()
}, ms)
}, deps)
const cancel = () => {
clearTimeout(timeout.current)
timeout = null
}
return [cancel]
}
export default useDebounce
//节流
import { useEffect, useRef, useState } from 'react'
const useThrottle = (fn, ms = 30, deps = []) => {
let previous = useRef(0)
let [time, setTime] = useState(ms)
useEffect(() => {
let now = Date.now();
if (now - previous.current > time) {
fn();
previous.current = now;
}
}, deps)
const cancel = () => {
setTime(0)
}
return [cancel]
}
export default useThrottle