[react]之解读hook

作者: MJ 分类: react 发布时间: 2019-06-26 21:30

基于官方文档:https://react.docschina.org/docs/hooks-intro.html


打算用几天的时间来写一下react的新特性hook(其实也不算新了),首先梳理下关于hook的一些特点:

突然想起来了我的高中政治老师曾经教我们的解题思路:是什么?为什么?怎么做?那我们也这样的思路来梳理一下hook:

是什么:

有意思的是,你会发现官方文档每一个hook模块的第一行都是下边这句话,足以证明记着它,对我们来说很重要!!!

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

为什么:

Hook 解决了我们五年来编写和维护成千上万的组件时遇到的各种各样看起来不相关的问题。无论你正在学习 React,或每天使用,或者更愿尝试另一个和 React 有相似组件模型的框架,你都可能对这些问题似曾相识。

hook出现的原因:https://react.docschina.org/docs/hooks-intro.html#motivation

怎么做:

Hook 本质就是 JavaScript 函数,但是在使用它时需要遵循两条规则:

a:只在最顶层使用 Hook

不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。(如果你对此感到好奇,我们在下面会有更深入的解释。)

b:只在 React 函数中调用 Hook

不要在普通的 JavaScript 函数中调用 Hook。你可以:

1:在 React 的函数组件中调用 Hook

2:在自定义 Hook 中调用其他 Hook

遵循此规则,确保组件的状态逻辑在代码中清晰可见。

==================== 以下为正文=====================

最重要的是,Hook 和现有代码可以同时工作,你可以渐进式地使用他们。官方没有计划从 React 中移除 class!

State Hook

使用方式:我们只能在非class组件中使用hook,比如我们之前的无状态组件或者叫函数组件

import React, { useState } from 'react';
const HooksModes = () => {
  const [count,setCount] = useState(0);
}
export default HooksModes;

这里解释一下const [count,setCount] = useState(0),这里用用了es6的结构,其实我们还可以这样来实现,首先打印一下useState(0),你会看到他是一个数组,第一个值是state的初始值,第二个是提供的更改state的方法,count和setCount可以是任何你想设置的key值,但是建议使用目前的,这样可以更好的理解它的含义。

count:对应我们之前的class组件中的constructor中的this.state = { count:0 }

setCount:对应我们之前class组件中的this.setState({count:0})

const count = useState(0)[0]
const setCount = useState(0)[1]

useState(0)中的0,是我们设置的state的初始值,之前在this.state= {}中只能放在这个对象中,现在我们可以设置为任意类型,对象,数组,number,string,null等。

我们在读取state的时候直接用{ count }(class的时候{ this.state.count }),设置的时候就用setCount(1),或者{() => setCount(count + 1)}(class的时候this.setState({count:1}))

完整代码示例:

import React, { useState } from 'react';
const HooksModes = () => {
  const [count,setCount] = useState(0);
  return (
    <div>
      <h1>{ count }</h1>
      <button onClick={() => setCount(count + 1)}> + </button>
      <button onClick={() => setCount(count - 1)}> - </button>
    </div>
  )
}

export default HooksModes;

我们是可以使用多个 state 变量的:

// 声明多个 state 变量
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: '学习 Hook' }]);

注意:

useState中多个state,不会像class组件中的this.setState,总是替换,而不是合并。

Effect Hook

Effect Hook 可以让你在函数组件中执行【副作用】操作。

副作用:数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用还是不理解?

副作用(Side Effect)是指一个 function 做了和本身运算返回值无关的事,比如:修改了全局变量、修改了传入的参数、甚至是 console.log(),所以 ajax 操作,修改 dom 都是算作副作用的;
side effect副作用是:和真实的世界进行交互的方式,就叫做 side effect,比如:用户点击了 保存 按钮,然后会异步地调用服务器的接口去保存数据。

忽然发现这里又延伸了一个概念:纯函数,大家看一下下边的文章,就能更好的理解这个概念

1:什么是纯函数?

2:怎么理解react的副作用?

3:怎么理解react的副作用?

如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

先牢记上边这一句话,学完之后你就会彻底理解它了。

在 React 组件中有两种常见副作用操作:需要清除的和不需要清除的,我个人理解是:

class组件中,在componentWillUnmount这个生命周期里边要做的就属于需要清除的(比如在这个生命周期里边清除settimeout定时器,重置变量等清除工作,防止引起内存泄漏),其他的就属于不需要清除的。

我们现在改一下计数的需求,每次点击的时候,把次数也显示在页面的title中

示例代码:

import React, { useState,useEffect } from 'react';
const HooksModes = () => {
  const [count,setCount] = useState(0);
  useEffect(() => {
    document.title = `你点击了${ count }次!`
  })
  return (
    <div>
      <h1>{ count }</h1>
      <button onClick={() => setCount(count + 1)}> + </button>
      <button onClick={() => setCount(count - 1)}> - </button>
    </div>
  )
}

export default HooksModes;

跟之前的class组件做一下对比,如果之前实现需要这样做:在两个生命周期里边去写两次代码

componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
}
componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
}

现在想一下之前说的一句话

如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

我们来分析一下这个函数如何集成了三个生命周期,以及如何区分的。

1:默认

useEffect(() => {
    document.title = `你点击了${ count }次!`
})

如果是默认情况,它会自动执行之前class组件的componentDidMount,componentDidUpdate这两个生命周期的方法

2:传入第二个参数为[]

useEffect(() => {
    document.title = `你点击了${ count }次!`
},[])

这种情况下,就相当于class组件中的componentDidMount,只在初始化的时候执行一次,不会重复执行。

3:传入第二个参数为[count],传入一个可变的变量或者可变的值

useEffect(() => {
    document.title = `你点击了${ count }次!`
},[count])

这种情况下,相当于class组件的componentDidMount,componentDidUpdate,我们的更新依赖于这可变的count(对比class组件的componentDidUpdate生命周期)

4:useEffect中返回一个函数(这个函数可以是箭头函数,可以是普通函数,这个函数名可以是任意符合规范的名称,建议跟自己的功能相对应),对应class组件里边的componentWillUnmount

useEffect(() => {
    document.title = `你点击了${ count }次!`;
    return () => {
      setCount(0);
    }
},[count])

我们在这个返回的函数中,处理我们之前在class组件中componentWillUnmount这个生命周期处理的逻辑就行了。

注意:

effect 在每次重渲染时都会执行,而不是只在卸载组件的时候执行一次。

为什么每次更新的时候都要运行Effect?

Hook 规则

官方提供了一个 linter 插件来强制执行这些规则

npm install eslint-plugin-react-hooks --save-dev
yarn add eslint-plugin-react-hooks --dev

1:只在最顶层使用 Hook

2:只在 React 函数中调用 Hook

自定义 Hook

通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。

目前为止,在react中有两种比较流行的方式来共享组件之间的状态逻辑,render props和高阶组件HOC,现在在hook中来处理相同的逻辑,就是自定义hook。

自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。

示例代码:

import React,{ useState } from 'react';
const useHk= (init) => {
  const [count,setCount] = useState(init);
  function action(){
    setCount(count + 1);
  }
  return [count,action];
}
export default useHk;

上边就是我们自定义的一个hook,我们把状态count和方法action,以数组的形式返回出去。其实看到这里的自定义函数,就能更好的理解useState的结构了。

我们在使用自定义hook的时候,直接引入刚才自定义的useHk,然后也用数组结构的形式来使用。

示例代码:

import React from 'react';
import useHk from './useHk';
const HooksModes = () => {
  const [count,action] = useHk(1);
  return (
    <div>
      <h2>{ count === 1 ? "11111" : "00000"}</h2>
    </div>
  )
}

export default HooksModes;

其他 Hook

以上两个hook是最常用的,也是比较重要的,当然了还有很多其他的hook,基础的Hook还有一个useContext,也做一下讲解。

我们之前在处理数据传输的时候,会用到React.createContext(),先看下示例:

示例代码:

import React,{ Component } from 'react';
import Hook from './hook';
export const contexts = React.createContext();
class HooksMode extends Component{
  constructor(props){
    super(props);
    this.state = {
      name:{
        name:"张大山",
       age:18
      }
    }
  }
  render(){
    return(
      <div>
        <contexts.Provider value={this.state.name}>
         <Hook /> 
        </contexts.Provider>
      </div>
    )
  }
} 

export default HooksMode;
import React, { useContext } from 'react';
import { contexts } from './hooks';
const HooksModes = () => {
  const { name } = useContext(contexts);
  return (
    <div>
      <h2>{ name }</h2>
    </div>
  )
}

export default HooksModes;

useContext(contexts)相当于class组件中的<contexts.Consumer>{(context) => {}}</contexts.Consumer>

欢迎关注小程序,感谢您的支持!

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注