react hooks学习笔记(一)

React Hooks简介

React Hooks是在React 16.8版本引入的全新API,程序员可以在不编写class的情况下使用state以及其他React中的特性。在16.8版本之前,组件的标准写法是类,也就是class。

hook为已知的react概念提供了更为直接的API:props、state、context、refs以及一些新的生命周期钩子函数。

Hook概览

当我们在编写函数组件并意识到需要向组件中添加以下state时,现在可以在现有的函数组件中使用Hook

Hook是一些可以让你在函数组件中“钩入”React state及生命周期等特性的函数。hook不能再class组件中使用。

State Hook

按照官方文档中计数器例子做一个小demo。
在这里插入图片描述
**方法一:**使用class方式

export default class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      count:0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={()=>{
          this.setState({
            count:this.state.count+1
          })
        }}>Click me</button>
      </div>
    )
  }
}

**方法二:**使用

import {useState} from 'react'
//引入 React 中的 useState Hook。它让我们在函数组件中存储内部 state。
function App() {
  let [count,setCount] = useState(0);
  // useState会返回一对值,当前的状态count和一个可以更新count的函数setCount,并将初始的state设置为0。
  return (
    <div className="App">
    <p>You clicked {count} times</p>
    <button onClick={()=>{
      setCount(count++)
    }}> click me 
    </button>
    </div>
  );
}

export default App;

我们也可以在一个组件中多次使用State Hook

function Example(){
  const [age,setAge] = useState(20);
  //设置年龄的初始值为20
  const [fruit, setFruit] = useState('apple')
  const [todos, setTodos] = useState({text:'Learning'})
  //初始值可以设置为number、string、object、boolean...
}

数组解构的语法让我们在调用useState时可以给state变量取不同的名字。
那么我们在调用useState方法的时候,React做了些什么呢?
首先,它会定义一个“state变量”,这里我们起名叫做count,这个count和class中的this.state中的count功能完全相同。但是一般来说在函数退出后变量就会消失,而state中的变量会被React保留。
useState的参数有哪些?
useState()方法里面唯一的参数就是初始State,如果我们想要在state中存储两个不同的变量,只需要调用两次setState即可。
useState返回的值是什么?
返回值为当前的state和更新state的函数。state只会在组件首次渲染的时候被创建。

Effect Hook

Effect Hook 可以让你在函数组件中执行副作用操作。数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用。

useEffect就是一个Effect Hook,给函数组件中增加了操作副作用的能力。她跟class中的componentDidMount(挂载后)、componentDidUpdate(更新后)、componentWillUnmount(卸载前)有相同的用途,只不过被合成了一个新的api。

react组件中有两种常见副作用操作:需要清除的和不需要清除的。

无需清除的effect

当我们只想在React更新Dom之后运行一些额外的代码。比如发送网络请求,手动变更DOM,记录日志等等这些都是无需清除的effect。

按照官方文档中计数器例子做一个小demo。
方法一:

export default class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      count:0
    };
  }
//在这个class中,我们需要在两个生命周期函数中编写重复的代码,这是因为我们希望组件加载和更新时执行同样的操作。也就是我们希望它在每次渲染之后执行。
  componentDidMount(){
    document.title = `You Clicked ${this.state.count} times`
  }
  componentDidUpdate(){
    document.title = `You Clicked ${this.state.count} times`
  }
  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={()=>{
          this.setState({
            count:this.state.count+1
          })
        }}>Click me</button>
      </div>
    )
  }
}

**方法二: **

function App() {
  let [count, setCount] = useState(0);
  // useState会返回一对值,当前的状态count和一个可以更新count的函数setCount,并将初始的state设置为0。
  useEffect(()=>{
    document.title = `You clicked ${count} times`
  })
  //我们可以在 effect 中获取到最新的 count 值,因为他在函数的作用域内。当 React 渲染组件时,会保存已使用的 effect,并在更新完 DOM 后执行它。这个过程在每次渲染时都会发生,包括首次渲染。
  return (
    <div className="App">
    <p>You clicked {count} times</p>
    <button onClick={()=>{
      setCount(count+1)
    }}> click me 
    </button>
    </div>
  );
}

useEffect做了些什么呢?
通过使用这个hook,你可以告诉React组件需要在渲染之后执行哪些操作。React会保存你传递的函数(称之为Effect),并且在执行dom更新之后调用它,在这个effect中,我们设置了document的title属性,也可以执行数据获取或调用其他命令式的API。
为什么会在组件内部调用useEffect?
将useEffect放在组件内部让我们可以在effect中直接访问count state变量或者其他的props。不需要使用其他的特殊API来读取它,它已经被保存在了函数的作用域中。Hook 使用了 JavaScript 的闭包机制,而不用在 JavaScript 已经提供了解决方案的情况下,还引入特定的 React API。
useEffect会在每次渲染后都执行吗?
是的,在默认情况下,它在第一次渲染之后和每次更新之后都会执行。你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。

需要清除的effect

订阅外部数据源这种情况下,清除工作是非常重要的,可以防止内存泄漏。
例如,假设我们有一个 ChatAPI 模块,它允许我们订阅好友的在线状态。 订阅和显示该状态
方法一:

import React, { Component } from 'react';

export default class Clear extends React.Component {
    constructor(props) {
      super(props);
      this.state = { isOnline: null };
      this.handleStatusChange = this.handleStatusChange.bind(this);
    }
  
    componentDidMount() {
      ChatAPI.subscribeToFriendStatus(
        this.props.friend.id,
        this.handleStatusChange
      );
    }
    componentWillUnmount() {
      ChatAPI.unsubscribeFromFriendStatus(
        this.props.friend.id,
        this.handleStatusChange
      );
    }
    handleStatusChange(status) {
      this.setState({
        isOnline: status.isOnline
      });
    }
 //componentWillUnmount和componentDidMount函数内部做的操作一模一样。
    render() {
      if (this.state.isOnline === null) {
        return 'Loading...';
      }
      return this.state.isOnline ? 'Online' : 'Offline';
    }
  }

方法二:

import React, { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

为什么在effect种返回一个函数?
每个effect都可以返回一个清除函数,如此可以将添加和删除订阅的逻辑放在一块,它们都属于effect的一部分。
React如何清除effect?
React会在组件卸载的时候执行清除操作。

useEffect需要注意的地方:
1.每次更新的时候都要运行 Effect
2.通过跳过Effect进行性能优化。

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新

上面这个示例中,我们传入 [count] 作为第二个参数。这个参数是什么作用呢?如果 count 的值是 5,而且我们的组件重渲染的时候 count 还是等于 5,React 将对前一次渲染的 [5] 和后一次渲染的 [5] 进行比较。因为数组中的所有元素都是相等的(5 === 5),React 会跳过这个 effect,这就实现了性能的优化。

当渲染时,如果 count 的值更新成了 6,React 将会把前一次渲染时的数组 [5] 和这次渲染的数组 [6] 中的元素进行对比。这次因为 5 !== 6,React 就会再次调用 effect。如果数组中有多个元素,即使只有一个元素发生变化,React 也会执行 effect。

对于清除操作的effect同样适用:

useEffect(() => {
  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
  return () => {
    ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  };
}, [props.friend.id]); // 仅在 props.friend.id 发生变化时,重新订阅

但是如果你要使用此优化方式,必须确保数组中包含所有外部作用域会随事件变化,并且在effect种使用的变量。

如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。这并不属于特殊情况 —— 它依然遵循依赖数组的工作方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值