hooks

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}
    </>
  )
}

posted on 2022-10-08 00:35  In-6026  阅读(43)  评论(0)    收藏  举报

导航