useState
const [count, setCount] = useState<number>(10)
// 必须用setCount更新count,但是setCount是一个异步函数,在频繁地操作时会有一些问题
<button onClick={
() => {
for (let i=1; i<=10; i++) {
setCount(count + i)
}
}
}>
展示的是最后一次的结果,结果是19,这样问题还不大,但是这样
<button onClick={
() => {
for (let i=1; i<=10; i++) {
setCount(count + 1)
}
}
}>
这样就会导致结果是11,与预期不符
这种频繁地操作,需要这样写:(当setCount中传入的是函数的时候,它会自动传入count作为参数,并执行该函数),相当于是 (c) => c + 1;传入count,返回的就是count+1,setCount(count + 1)
<button onClick={
() => {
for (let i=1; i<=10; i++) {
setCount((c) => c + 1) // 这个形参代表的是count,因为setCount会把count做参数传参给该函数,形成闭包
}
}
}>
useReducer
// 第一个变量是funtion,专门用来修改值,就像vuex的mutation,但是必须return,return的才是新的值,而不是像mutation单纯的修改
// 第二个参数是初始化值
// 第三个参数是funtion,以第二个参数作为自己的参数,必须有返回值,返回值就是reducer定义的响应式变量的初始值,这个参数可以不传
import { useReducer } from 'react'
interface IReducerState {
name: string
gender: string
}
function reducer(state: IReducerState, action: Record<string, any>): IReducerState {
switch (action.type) {
case 'change_last_name':
return { ...state, name: 'Z' + state.name.slice(1) }
default:
throw new Error(action.type + ' is not a right action')
}
}
const init = (args: IReducerState): IReducerState => args
export default function App() {
const [person, dispacth] = useReducer(reducer, { name: 'LLC', gender: 'meal' }, init)
return (
<>
<h2>name: {person.name}, gender: {person.gender}</h2>
<button onClick={() => {
dispacth({ type: 'change_last_name' })
}}>click me</button>
</>
)
}
useCallback + memo优化
//当函数组件中state变化,都会重新运行函数,重新渲染;
//重新渲染事小,重新运行函数事大,这会导致子组件也跟着刷新,影响性能
//因此需要useCallback + memo手动优化
当props里不包括函数的时候,用memo就够了,可以避免父组件rerender带动子组件做无意义rerender
funtion Father() {
const [age, setAge] = useState(30)
const [sonName, setSonName] = useState('Son')
return (
<>
<h1 onClick={()> {
setAge(age + 1) // 这里改变本来不关Son的事,但是会引起Father rerender,进而引起Son rerender,因此需要memo
}}>Father is {age} years old</h2>
<Son name={sonName} />
</>
)
}
const Son = memo(funtion(props) {
return <h2>{props.name}</h2>
})
当props里有函数时需要用到useCallback包裹
// Father每次rerender都会创建新的funtion,此时的和上次props里传的已经不是用一个function了,所以会导致子组件的rerender
funtion Father() {
const aFunc = useCallbace(() => {
//...
}, [])
return <Son fun={aFunc} />
}
const Son = memo(funtion() {
//...
})
注解:
const aFunc = useCallbace(() => {
console.log(age) // 函数里面要使用依赖里的东西,否则ts报错;“因为age而不同,但根本没age的事”,是不合逻辑的
}, [age]) //此处可以传依赖,只要age没变,Son就不会rerender;
useMemo,类似vue中computed
export default function App() {
const [a, setA] = useState(0)
const [b, setB] = useState(0)
const [c, setC] = useState(0)
// 在每次a,b变更后返回a+b,这样才能动态计算a + b
const m = useMemo(() => {
return <p>this is a memo compnent {a + b}</p>
}, [a, b])
return (
<>
<p>a: {a}</p>
<p>b: {b}</p>
<p>c: {c}</p>
<button onClick={() => { setA(a + 1) }}>setA</button>
<button onClick={() => { setB(b + 1) }}>setB</button>
<button onClick={() => { setC(c + 1) }}>setC</button>
<p>{a + b}</p> 这样虽然也行,但是当c更新时,会引起rerender,对这一行来说是无意义的,是浪费资源,损耗性能,因为它不依赖c;
{m}
</>
)
}