React Hooks

208 阅读3分钟

React Hooks

介绍

1.1 React Hooks 是用来做什么的

对函数型组件进行增强, 让函数型组件可以存储状态, 可以拥有处理副作用的能力.让开发者在不使用类组件的情况下, 实现相同的功能.

2.1 useState

  1. 接收唯一的参数即状态初始值. 初始值可以是任意数据类型.
  2. 返回值为数组. 数组中存储状态值和更改状态值的方法. 方法名称约定以set开头, 后面加上状态名称.
  3. 方法可以被调用多次. 用以保存不同状态值.
  4. 参数可以是一个函数, 函数返回什么, 初始状态就是什么, 函数只会被调用一次, 用在初始值是动态值的情况.

useState 设置状态值

  1. useState() 设置状态值方法的参数可以是一个值也可以是一个函数
  2. 设置状态值方法的方法本身是异步的
import { useState } from 'react'

function App (props) {

  const [count, setCount] = useState(() => {
    // 初始执行一次
    return props.count || 0
  })
  const [person, setPerson] = useState({
    name: 'zhang',
    age: 10
  })

  function handleCount () {
    setCount(count => {
      const newCount = count + 1
      document.title = newCount
      return newCount
    })
  }

  return (
    <div className="App">
      <p>{count}</p>
      <p>{person.name}</p>
      <p>{person.age}</p>
      <button onClick={handleCount}> +1 </button>
      <button onClick={() => setPerson({ name: 'ss', age: 0 })}>setPerson</button>
    </div>
  );
}

2.2 useReducer

useReducer是另一种让函数组件保存状态的方式.


function reducer (state, action) {
 switch (action.type) {
   case 'increment':
     return state + 1
   case 'decrement':
     return state - 1

   default:
     break;
 }
}

const [useReducerCount, dispatch] = useReducer(reducer, 0)


<div>
 <p>useReducer</p>
 <p>{useReducerCount}</p>
 <button onClick={() => dispatch({ type: 'increment' })}>useReducerCount + 1</button>
 <button onClick={() => dispatch({ type: 'decrement' })}>useReducerCount - 1</button>
</div>

2.3 useContext

useContext()在跨组件层级获取数据时简化获取数据的代码.


// 组件
function Foo () {
  // 简化
  const fooCount = useContext(countContext)
  return <div>
    {fooCount}
  </div>
}

<div>
  <countContext.Provider value={100}>
    <Foo />
  </countContext.Provider>
</div>

2.4 useEffect()

2.4.1 useEffect() 执行时机

让函数型组件拥有处理副作用的能力. 类似生命周期函数.

  1. useEffect 执行时机可以把 useEffect 看做 componentDidMount, componentDidUpdate 和 componentWillUnmount 这三个函数的组合.
  • useEffect(() => {}) => componentDidMount, componentDidUpdate
  • useEffect(() => {},[]) => componentDidMount
  • useEffect(() => () => {}) => componentWillUnMount

2.4.2 useEffect 解决的问题

  1. 按照用途将代码进行分类 (将一组相干的业务逻辑归置到了同一个副作用函数中)
  2. 简化重复代码, 使组件内部代码更加清晰

2.4.3 useEffect 结合异步函数


useEffect(() => {
  // 自执行函数
    (async function () {
      let res = await getData()
      console.log(res)
    })()
  }, [])

  function getData () {
    return new Promise((resolve, reject) => {
      resolve({ msg: 'Hello' })
    })
  }

2.4.4.指定数据变化触发 useEffect


useEffect(() => {
    console.log('sss')
    document.title = count
  }, [count])

2.5 useMemo()

  1. useMemo 的行为类似Vue中的计算属性, 可以监测某个值的变化, 根据变化值计算新值.
  2. useMemo 会缓存计算结果. 如果监测值没有发生变化, 即使组件重新渲染, 也不会重新计算. 此行为可以有助于避免在每个渲染上进行昂贵的计算.

  const [useMemoCount, setUseMemoCount] = useState(0)

  function handlerSetUseMemoCount () {
    setUseMemoCount(useMemoCount => {
      return useMemoCount + 1
    })
  }

  const double = useMemo(() => {
    console.log('1')
    return useMemoCount * 2
  }, [useMemoCount])


  <div>
    <p> useMemo </p>
    <p> {useMemoCount} {double}</p>
    <p>{bool ? '真' : '假'}</p>
    <button onClick={handlerSetUseMemoCount}>handlerSetUseMemoCount</button>
    <button onClick={() => setBool(!bool)}>setBool</button>
  </div>

2.6 memo ⽅法 性能优化, 如果本组件中的数据没有发⽣变化, 阻⽌组件更新

2.7 useCallback 性能优化, 缓存函数, 使组件重新渲染时得到相同的函数实例


 // 不会重新渲染
  const resetCount = useCallback(() =>
    setCount(0), [setCount]
  )

  <div>
    <p>Zoo</p>
      useCallback 防止点击 resetCount Zoo组件重新渲染
    <Zoo resetCount={resetCount} />
  </div>


  const Zoo = memo(function Zoo (props) {
  console.log('重新渲染')
  return <div>
    我是zOO
  <button onClick={props.resetCount}>resetCount</button>
  </div>
})

2.8 useRef()

获取DOM元素对


  const useName = useRef()

  function handler () {
    console.log(useName, 'useName')
  }

  <div>
    <p>useRef</p>
    <input ref={useName} onChange={handler}></input>
  </div>

保存数据 (跨组件周期) 即使组件重新渲染, 保存的数据仍然还在. 保存的数据被更改不会触发组件重新渲染


const UseRef = memo(function UseRef () {
  const [count, setCount] = useState(0)
  let timerId = useRef()
  useEffect(() => {
    timerId.current = setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
  }, [])

  const stopCount = () => {
    console.log(timerId)
    clearInterval(timerId.current)
  }

  return <div>
    我是useRef
    <div>{count}</div>
    <button onClick={stopCount}>停止</button>
  </div>
})

3. 自定义 HOOK 不共享

  • ⾃定义 Hook 是标准的封装和共享逻辑的⽅式.
  • ⾃定义 Hook 是⼀个函数, 其名称以 use\color{red}{use} 开头.
  • ⾃定义 Hook 其实就是逻辑和内置 Hook 的组合

// 自定义hooks

function useGetPost () {
  const [post, setPost] = useState({})
  useEffect(() => {
    axios.get('https://jsonplaceholder.typicode.com/posts/1')
      .then(res => {
        setPost(res.data)
      })
  }, [])

  return [post, setPost]
}

const Hooks = memo(function Hooks (params) {

  const [post] = useGetPost()

  return <div>
    Hooks
    <div>{post.title}</div>
    <div>{post.body}</div>
  </div>
})



function useUpdateInput (initialValue) {
  const [value, setValue] = useState(initialValue)
  return {
    value,
    onChange: event => setValue(event.target.value)
  }
}


const From = memo(function From (params) {
  const usernameInput = useUpdateInput('')
  const passwordInput = useUpdateInput('')
  const submitForm = event => {
    event.preventDefault();
    console.log(usernameInput.value, passwordInput.value)
  }
  return <div>
    <form onSubmit={submitForm}>
      <input type="text" name="username" {...usernameInput} />
      <input type="password" name="password" {...passwordInput} />
      <input type="submit" />
    </form>
  </div>
})