React Hooks的深入学习

之前有在React Hooks基础这篇文章中浅浅谈了一下Hooks,这篇文章就把新旧知识点进行一个整合,更加深入地去学习React Hooks。

React Hooks是React 16.8 版本推出的新特性,目的在于解决React的状态共享与生命周期管理混乱的问题。React Hooks的出现,就标志着React不会存在无状态组件的情况,只有类组件和函数组件。

一、基础Hook

1. useState()

基础的用法这里就不细讲了,这里大致过一下~

useState() 用于启用函数组件中的状态,第一个参数是状态的初始值。
[state, setState] = useState(initialValue):数组中是状态值和更新状态函数。
一个组件中可以有多个状态。

当初始状态需要昂贵的性能方面操作时,可以在初始化的时候进行:

function Demo() {
  const [state, setState] = useState(function demo() {
    //expensive operation
  })
}

函数demo()仅在初始化时渲染一次,不参与后续组件的渲染,可以利用这一特点提升组件性能。

关于过时状态的处理:

function Demo() {
  const [count, setCount] = useState(0)
  const handleClick = () => {
    setTimeout(function delay() {
      setCount(count + 1)  //改为:setCount(count => count + 1)
    }, 3000)
  }
  return <div>{count}</div>
}

此时的count数与我们想要的结果不一致,因为delay是一个过时的闭包,捕获的是一个过时的变量,可以使用函数方法来更新状态,改为 setCount(count => count + 1)。

关于复杂状态的管理,会在useReducer中讲到。

2. useEffect()

正常情况下,网络请求、模块订阅以及DOM操作都是属于副作用操作的范畴,官方并不建议在函数体中书写副作用代码,而 Effect Hook 就是用于解决这一问题的,它可以允许你在函数组件中执行一些副作用操作。

可以将 useEffect Hook 看作是componentDidMount、componentDidUpdate 和 componentWillUnmount 的组合。

在class组件中,我们一般希望组件在加载和更新时执行同样的操作,所以导致了我们需要在两个生命周期函数中编写相同的代码。而 useEffect() 一般情况下在第一次渲染之后和每次更新之后都会执行,不需要考虑是挂载还是更新,并且React在保证运行effect的同时,DOM都已经更新完毕。

useEffect 是一个接受两个参数的函数,第一个参数为一个叫 effect 的函数,在组件第一次渲染时被执行,第二个参数是一个存储依赖关系的数组。

关于在async await时报错:

每个async函数都会默认返回一个隐式的promise,但是useEffect不应该返回任何内容,可以采用不直接调用async来解决:

function Demo() {
  const [data, setData] = useState(//根据类型定义)
  useEffect(() => {
    const getData = async () => {
      const res = await axios('接口')
      setData(res.data)
    }
    getData()
  }, [])
  return (//省略)
}

3. useContext()

Context 提供了一个无需为每层组件手动添加props就能在组件树间进行数据传递的方法。

使用 React Context API,在组件外部建立一个 Context。

import React, { useContext } from "react";
import ReactDOM from "react-dom";
import "./styles.css";

const AppContext = React.createContext({});

useContext()钩子函数用来引入 Context 对象,从中获取username属性。

const Navbar = () => {
  const { username } = useContext(AppContext)

  return (
    <div className="navbar">
      <p>AwesomeSite</p>
      <p>{username}</p>
    </div>
  )
}

const Messages = () => {
  const { username } = useContext(AppContext)
  return (
    <div className="messages">
      <h1>Messages</h1>
      <p>1 message for {username}</p>
      <p className="message">useContext is awesome!</p>
    </div>
  )
}

封装组件代码
AppContext.Provider提供了一个 Context 对象,这个对象可以被子组件共享

function App() {
  return (
    <AppContext.Provider value={{
      username: 'superawesome'
    }}>
      <div className="App">
        <Navbar />
        <Messages />
      </div>
    </AppContext.Provider>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

4. useReducer()

useReducer主要用于在某种复杂环境下替换useState,例如包含复杂逻辑的state并且包含多个子值,或是后面的state依赖前面的state等。

useReducer语法如下:

const[state, dispatch] = useReducer(reducer, initialArg, init)

关于复杂状态的管理,使用useState:

function Demo() {
  const [person, setPerson] = useState([{name: 'Jack'}])
  const addPerson = p => setPerson([...p, person])
  const removePerson = index => setPerson([
    ...person.slice(0, index),
    ...person.slice(index + 1)
  ])
  return (
    // 此处省略
  )
}

更好的解决方案是将复杂的状态管理提取到 reducer 中:

function reducer(state, action) {
  switch (action.type) {
    case 'add':
      return [...state, ation.item];
    case 'remove':
      return [
        ...state.slice(0, action.index),
        ...state.slice(action.index + 1)
      ];
    default:
      throw new Error();
  }
}

function Demo() {
  const [state, dispatch] = useReducer(reducer, [{name: 'Jack'}])
  return (
    // dispatch({ type: 'add', item: person })
    // dispatch({ type: 'remove', item: index })
  )
}

5. useMemo()

在类组件中,每次状态的更新都会触发组件树的重绘,带来不必要的开销。同样,在函数组件中,为了避免useState每次渲染造成的开销, React Hooks推出了useMemo函数。

useMemo之所以能带来性能上的提升,是因为在依赖不变的情况下,会返回相同的引用,避免子组件进行无意义的重复渲染。

下面代码是使用普通useState实现的功能:

function Demo() {
  const [count, setCount] = useState(1)
  const [value, setValue] = useState('')
  function expensive() {
    let sum = 0
    for (let i = 0; i < count * 10; i++) {
      sum += i
    }
    return sum
  }

  return (
    <>
      {count} : {expensive()}
      <button onClick={() => setCount(count + 1)}>+</button>
      <input value={value} onChange={e => setValue(e.target.value)} />
    </>
  )
}

在上面的例子中,修改count和value的值,都会触发expensive方法,但是expensive只是依赖于count,所以更新value是没必要执行expensive的。

可以使用useMemo来优化:

function Demo() {
  const [count, setCount] = useState(1)
  const [value, setValue] = useState('')
  const expensive = useMemo(() => {
    let sum = 0
    for (let i = 0; i < count * 10; i++) {
      sum += i
    }
    return sum
  }, [count])

  return (
    <>
      {count} : {expensive()}
      <button onClick={() => setCount(count + 1)}>+</button>
      <input value={value} onChange={e => setValue(e.target.value)} />
    </>
  )
}

这样经过处理后,只会在count更新时触发expensive方法,减少了性能上的开销。

6. useCallback()

和useMemo一样,useCallback 也是用来减少性能开销的,即当依赖的状态改变时,才会调用回调函数。但与之不同的是,useMemo 是用来缓存计算结果的数值的,而 useCallback 缓存的是函数。

一般来说,父组件发生更新,那么子组件也会执行更新,但是在大多数情况下,子组件的更新是没有必要的。所以可以使用useCallback返回缓存的函数,并把这个缓存的函数作为props传递给子组件。

function Parent() {
  const [count, setCount] = useState(1)
  const [value, setValue] = useState('')
  const callback = useCallback(() => {
    return count
  }, [count])

  return (
    <>
      {count}
      <Child callback={callback} />
      <button onClick={() => setCount(count + 1)}>+</button>
      <input value={value} onChange={e => setValue(e.target.value)} />
    </>
  )
}
function Child({callback}) {
  const [count, setCount] = useState(() => callback())
  useEffect(() => {
    setCount(callback())
  }, [callback])
}
return (
  <div>{count}</div>
)

7. useRef()

Ref的主要作用是获取实例或者DOM元素,创建Ref主要有两种方式:creatRef、useRef。

用 creatRef 创建的 Ref 每次渲染都会返回一个新的引用,而 useRef 每次渲染都会返回相同的引用。

使用 creatRef 的方式创建 Ref 主要是类组件:

class Demo extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.creatRef()
  }
  componentDidMount() {
    this.myRef.current.focus()
  }
  render() {
    return <input ref={this.myRef} type='text' />
  }
}

使用 useRef 创建 Ref:

function Demo() {
  const myRef = useRef(null)
  useEffect(() => {
    myRef.current.focus()
  }, [])
  return <input ref={myRef} type='text' />
}

二、自定义Hook

在React中,自定义Hook是一个以use开头的函数,函数内部可以调用其他的hook,并且使用自定义hook时,入参和返回值都是可以自定义的,没有其他特殊约定。

定义一个自己的hook:

function useMyHooks() {
  const [demo, setDemo] = useState()
  useEffect(() => {
    //省略
  }, [])
  // return
}

使用这个hook:

const a = useMyHooks()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值