Hooks就是在不编写class的情况下使用state等其他的React特性。
- 只能在 [ 函数最顶层 ] 调用Hook。
- 只能在 [ React的函数组件 ] 中调用Hook,不能在普通函数 也不能是class组件。
useState()
使用
import { useState } from "react";
function UseState() {
const [stu, setStudent] = useState({ count: 0, age: 18 });
const [search, setSearch] = useState(0);
function add() {
setSearch(search + 1)
}
function sub() {
setSearch(search - 1)
}
return (
<div>
<button onClick={add}>+</button>
{search}
<button onClick={sub}>-</button>
</div>
);
}
export default UseState
与class中的setState不同的是,不会合并state中的值。
import { useState } from "react";
function UseState() {
const [stu, setStudent] = useState({ count: 0, age: 18 });
const [search, setSearch] = useState(0);
function add() {
setStudent({
...stu,
count: state.count + 1
})
}
function sub() {
setStudent({
...stu,
count: state.count - 1
})
}
return (
<div>
<button onClick={add}>+</button>
{stu.count}
<button onClick={sub}>-</button>
</div>
);
}
export default UseState
prevState
setCount(prevState=>prevState+10)
useEffect
用于实现类似生命周期。在刘阿龙年起完成布局与绘制好后传给useEffect的函数会延迟调用。所以它适合用于许多常见的 副作用场景。
副作用
纯函数 产生确定的输出,与执行次数和时间无关。
副作用 http请求、系统IO相关API、函数体内修改函数外变量、Date.now()等
使用
//只有第一个参数是函数的时候,刷新和加载组件的时候都会执行。
useEffect(()=>{console.log("1")})
//第二个参数,传递的是一个空数组,只有执行初始化和卸载的代码,不触发更新。
useEffect(()=>{console.log("1")},[])
//第二个参数,传递有值的数组,当值改变的时候就会重新渲染组件。
useEffect(()=>{console.log("1")},[source])
执行顺序
useEffect(()=>{console.log("1")})
console.log('2');
useEffect(()=>{console.log("3")},[])
执行结果顺序为:2->1->3
useLayoutEffect
与useEffect的对比
useEffct执行顺序:组件更新挂载完成->浏览器dom绘制完成->执行useEffect回调。
useLayoutEffect执行顺序:组件更新挂载完成->执行useLayoutEffect回调->浏览器dom绘制完成。
import React from 'react'
export default function useLayoutEffect() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log({ count });
const pre = Data.now();
// 等待了一秒
while (Date.now() - pre < 1000) { }
if (count === 0) {
setCount(10 + Math.random() * 200);
}
}, [count])
return (
<div onClick={() => { setCount(0) }}>{count}</div>
)
}
//可以发现会打印两次count,使用useEffect会闪烁
//改为useLayoutEffect就不会闪频
export default function useLayoutEffect() {
const [count, setCount] = useState(0);
useLayoutEffect(() => {
console.log({ count });
const pre = Data.now();
// 等待了一秒
while (Date.now() - pre < 1000) { }
if (count === 0) {
setCount(10 + Math.random() * 200);
}
}, [count])
return (
<div onClick={() => { setCount(0) }}>{count}</div>
)
}
useRef
import React from 'react'
export default function useRef() {
const inputRef = useRef(null);
useEffect(() => {
console.log(inputRef.current.value);
})
return (
<input ref={inputRef} />
)
}
useContext
在函数中可以使用context,解决了provider和consumer需要而外包装组件的问题。
使用
//平台
import React from 'react'
export default React.createContext();
//父组件
import React from 'react';
import Context from './context';
import Children from './Children';
export default function father() {
const { Provider } = Context;
return (
<>
<Provider value={{ userId: 1 }}>
<Children />
</Provider>
</>
)
}
//子组件
import React from 'react';
import ChildrenChildren from './ChildrenChildren'
export default function Children() {
return (
<div>Children
<p>
<ChildrenChildren />
</p>
</div>
)
}
//子子组件
import React from 'react';
import context from './context';
export default function ChildrenChildren() {
const fatherContext = useContext(context);
console.log(fatherContext);
return (
<div>ChildrenChildren</div>
)
}
默认值
当父组件中没有加提供者<Provider value={{ userId: 1 }}></Provider>组件的时候
export default React.createContext({user:'默认值'});
useMemo | React.memo | useCallback
import { useState, useMemo, useEffect } from 'react'
export default function useMemo() {
const [name, setName] = useState('小明');
const [age, setAge] = useState(13);
const [sex, setSex] = useState('man')
return (
<div>
name: {name}-- age:{age} sex:--{sex}
<button onClick={() => { setName(name + '加一') }}>changeName</button>
<button onClick={() => { setAge(name + 1) }}>changeAge</button>
<button onClick={() => { setSex(name + ' man') }}>changeSex</button>
<br />
<useMemoChild name={name} age={age} sex={sex} />
</div>
)
}
// 子组件
function useMemoChild({ name }) {
useEffect(() => {
console.log('子组件更新了');
})
return (
<>
<h2>这是子组件</h2>
<p>{name}</p>
</>
)
}
我们可以发现父组件更新数据的时候,子组件会跟父组件一起更新。
但是就算子组件中没有传入的值改变时子组件也会再渲染,进行高开销计算。
useMemo
// 子组件
function useMemoChild({ name }) {
useEffect(() => {
console.log('子组件更新了');
})
const nameHandler = useMemo(() => {
return name
}, [name])
return (
<>
<h2>这是子组件</h2>
<p>{nameHandler}</p>
</>
)
}
我们可以发现使用useMemo子组件不会改,但是useEffect会继续打印内容。
React.memo
const useMemoChild = React.memo(({ name }) => {
useEffect(() => {
console.log('子组件更新了');
})
return (
<>
<h2>这是子组件</h2>
<p>{name}</p>
</>
)
})
useCallback
当子组件绑定事件的时候,由于函数的特性每次传过去的都是新的函数,所以需要使用到useCallback。
<useMemoChild onChange={()=>{}} />
export default function useMemo() {
const [name, setName] = useState('小明');
const [age, setAge] = useState(13);
const [sex, setSex] = useState('man');
const change = useCallback(() => {
},[name])
return (
<div>
name: {name}-- age:{age} sex:--{sex}
<button onClick={() => { setName(name + '加一') }}>changeName</button>
<button onClick={() => { setAge(name + 1) }}>changeAge</button>
<button onClick={() => { setSex(name + ' man') }}>changeSex</button>
<br />
<useMemoChild name={name} onClick={change} />
</div>
)
}