1.useState
1.做一个简单的计数器来了解useState()
const [count, setCount] = useState(0);
const num = 1;
const add = () => {
setCount(count + 1)
};
const remove = () => {
setCount(count - 1);
};
function App() {
return (
<div className="App">
<h1>{count}</h1>
<h1>{num}</h1>
<button onClick={add}>计数+</button>
<br />
<button onClick={remove}>计数-</button>
</div>
);
}
如果一个变量不用于JSX 中显示,那就不要用setState来管理它,使用useRef()
2.state --不可变数据
- props 父组件传递过来的数据
- state 组件内部的状态信息,不对外
- state变化, 触发组件的更新,重新渲染rerender页面
import React, { FC, useState } from 'react'
const UseStateDemo: FC = () => {
const [userInfo, setUserInfo] = useState({ name: 'A类', age: 20 })
const [arr, setArr] = useState(['a', 'b', 'c', 'd', 'e'])
const changeUserInfo = () => {
setUserInfo({
...userInfo,
age: 18,
})
}
const changeArrAdd = () => {
setArr(arr.concat('eqw'))
setArr(arr.push('eqw'))
setArr([...arr, 'eqw'])
}
return (
<div>
<h1>State 不可变数据</h1>
<span>不可变数据 不去修改state的值,而是要传入一个新的值 </span>
<h3>对象</h3>
<h2>{userInfo.name}</h2>
<h2>{userInfo.age}</h2>
<button onClick={changeUserInfo}>改变用户信息</button>
<br />
<h2>{arr.map(item => item + ' ')}</h2>
<button onClick={changeArrAdd}>改变数组</button>
</div>
)
}
export default UseStateDemo
1.异步更新
const [count, setCount] = useState(0);
const add = () => {
setCount(count + 1)
setCount(count + 1)
setCount(count + 1)
setCount(count + 1)
setCount(count + 1)
setCount(count => count + 1)
setCount(count => count + 1)
setCount(count => count + 1)
setCount(count => count + 1)
setCount(count => count + 1)
console.log(count, 'count')
};
2.useEffect
(1)副作用
函数组件:执行函数,返回 JSX
初次渲染时
state 更新时
但有些场景需要如下功能
当渲染完成时,做某些事情
当某个 state 变化时,做某些事情
如 ajax 加载数据(state 变化重新加载)
所以需要 useEffect
(2) 组件渲染完成时
useEffect(() => {
console.log('加载http请求')
}, [])
(3)state更新时
useEffect(() => {
console.log('list被改变')
}, [list])
(4)组件销毁时
useEffect(() => {
console.log('加载http请求')
return () => {
console.log('销毁')
}
}, [])
(5)为什么初次加载会执行俩次
useEffect(() => {
console.log('加载http请求')
}, [])
3.useRef
- 一般用于操作DOM
- 也可以传入普通的JS变量,但是不会触发render
- 必须要和Vue3的ref区分开,二者还是有很大的区别
- vue3中ref用于监听响应式数据
- react中ref用于获取DOM操作
1.操作DOM
export const UseRefDemo: FC = () => {
const inputRef = useRef<HTMLInputElement>(null)
const selectInput = () => {
console.log(inputRef.current, '----ref---')
const inputElem = inputRef.current
if (inputElem) inputElem.select()
}
return (
<div>
<input ref={inputRef} defaultValue="我是useRef" />
<button onClick={selectInput}>选中input</button>
</div>
)
}
2.定义变量
export const UseRefDemo: FC = () => {
const nameRef = useRef('我是useRef')
const changeName = () => {
nameRef.current = '2421412'
console.log(nameRef.current, '----nameRef.current----')
}
return (
<div>
<p>name: {nameRef.current}</p>
<button onClick={changeName}>改变</button>
</div>
)
}
4.useMemo
- 函数组件,每次state更新都会重新执行函数并且render
- useMemo 可以缓存数据, 不用每次执行函数都重新生成
- 可用于计算量较大的场景,缓存提高性能 === 空间换时间
- react官网中有一句话叫
你可以把 useMemo 作为性能优化的手段,但不要把它当成语义上的保证
zh-hans.reactjs.org/docs/hooks-…
export const UseMemoDemo: FC = () => {
console.log('render')
const [num1, setNum1] = useState(10)
const [num2, setNum2] = useState(20)
const [num3, setNum3] = useState(30)
const [str, setStr] = useState('我是useMemo')
const sum = useMemo(() => {
console.log('sum------')
return num1 + num2 + num3
}, [num1, num2, num3])
return (
<div>
<h1>UseMemoDemo</h1>
<p>{sum}</p>
<p>
{num1} <button onClick={() => setNum1(num1 + 1)}>Add Num1</button>
</p>
<p>
{num2} <button onClick={() => setNum2(num2 + 1)}>Add Num2</button>
</p>
<p>
{num3} <button onClick={() => setNum3(num3 + 1)}>Add Num3</button>
</p>
<p>
// 改变change的时候并没有触发 sum
<input value={str} onChange={e => setStr(e.target.value)} />
</p>
</div>
)
}
4.useCallback
- useCallback 就是 useMemo 的语法糖,和 useMemo 一样
export const UseCallbackDemo: FC = () => {
const [text, setText] = useState('useCallback')
const fn1 = () => console.log('fn1 useCallback', text)
const fn2 = useCallback(() => {
console.log('fn2 useCallback', text)
}, [text])
return (
<div>
<h1>UseCallbackDemo</h1>
<button onClick={fn1}>FN1</button>
<button onClick={fn2}>FN2</button>
<input value={text} onChange={e => setText(e.target.value)} />
</div>
)
}
5.自定义Hook
- 内置Hooks 保证了基础功能
- 内置Hooks 灵活配合,实现业务功能
- 抽离公共部分, 自定义Hooks 或者第三方Hooks---复用代码,提高效率
- 之前是Class 组件,现在是函数组件
- class组件: Mixin HOC render-props 来复用公共逻辑
- 函数组件: 使用Hooks --- 当前最完美的解决方案
1.修改网页标题Hook
const useTitle = (title: string) => {
document.title = title
}
使用 useTitle('标题')
2.获取鼠标位置
export const useMouse = () => {
const [x, setX] = useState(0)
const [y, setY] = useState(0)
const mouseMoveHandler = useCallback((event: MouseEvent) => {
setX(event.clientX)
setY(event.clientY)
}, [])
useEffect(() => {
window.addEventListener('mousemove', mouseMoveHandler)
return () => {
window.removeEventListener('mousemove', mouseMoveHandler)
}
}, [])
return {
x,
y,
}
}
使用: const { x, y } = useMouse()
3. 模拟异步加载数据
function getInfo(): Promise<string> {
return new Promise(resolve => {
setTimeout(() => {
resolve(Date.now().toString())
}, 1500)
})
}
export const useGetInfo = () => {
const [loading, setLoading] = useState(true)
const [info, setInfo] = useState('')
useEffect(() => {
getInfo().then(info => {
setLoading(false)
setInfo(info)
})
}, [])
return {
loading,
info,
}
}
使用: const { loading, info } = useGetInfo()
<span>{loading ? '加载中' : info}</span>
5.第三方hook
- ahooks 国内流行
https://ahooks.js.org/zh-CN/hooks/use-title/
- 功能全面
- 使用简单
- 文档 demo 清晰易懂
- React-use 国外流行
https://github.com/streamich/react-use
6.Hooks 使用规则
- 必须使用useXX格式命名
- 只能在两个地方调用hook,(组件内,其他hook内)
- 不能把Hook放在if for 内部, 必须保证每次调用的顺序一致
7.Hooks 闭包陷阱
- 当异步函数获取state时,可能不是当前最新的state
- 可使用useRef来解决
- 提前了解JS闭包
export const ClosureTrap: FC = () => {
const [count, setCount] = useState(0)
const countRef = useRef(0)
const add = () => {
setCount(count + 1)
}
const alertFn = () => {
setTimeout(() => {
alert(count)
}, 3000)
}
return (
<div>
<h1>闭包陷阱</h1>
<div>{count}</div>
<button onClick={add}>增加</button>
<button onClick={alertFn}>Alert</button>
</div>
)
}
1.先点击增加按钮
2.然后点击alert---再快速点击增加按钮
3.出现弹窗,弹窗内的值还是之前的值,setTimeout异步拿的不是点击增肌之后最新的值
4.然后可以使用useRef解决这个陷阱, 可以打开屏蔽代码,注释alert()值类型 体验一下