用实战带你走进react hooks:userState、 useEffect、useMemo

1111.png

React 16.8发布后带来了一项新的玩意,React发生了大改变!!!解决了很多类组件的固有缺陷,这篇文章让大家快速熟悉并掌握最常用的两个Hook:userState 和 useEffect
了解使用的过程中,还能掌握一些背后的原理,顺便实现一个豆瓣电影吧榜单应用。

准备工作

阅读之前,希望同学已经做了一些准备

  1. 掌握了react基础知识
  2. 会使用react来写代码

开始

Hook是什么?

Hook 是什么? Hook 是一个特殊的函数,它可以让你“钩入” React 的特性。例如,useState 是允许你在 React 函数组件中添加 state 的 Hook。稍后我们将学习其他 Hook。

为什么需要hooks?

1.hooks出现之前, 类组件提供了完整的状态管理和声明周期控制,通常用来承载复杂的业务逻辑。
2.函数组件则是纯粹的从数据到数据映射,对状态无感知。

hooks解决了什么问题?

1.令人头痛的this绑定
2.声明周期的不合理
3.数据共享的困局(需要借助复杂的状态管理器redux)

我们先看看官方介绍 Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

userState官方示例

import React, { useState } from 'react';

function Example() {
  // 声明一个新的叫做 “count” 的 state 变量
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

官方讲解的比较分散,链接:https://react.docschina.org/docs/hooks-intro.html,我会从实现一个简单项目来介绍使用

等价的 class示范

 class Example extends React.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>
     );
   }
 }

tip: 是不是觉得hooks代码清晰简单了很多

来看一个经典的计数器

function Counter() {
  const [count, setCount] = useState(0);

  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
      <button onClick={handleAlertClick}>
        Show alert
      </button>
    </div>
  );
}

这里问大家一个问题?

点击 Click me 按钮,count增加到 3, 点击 Show alert 按钮,在setTimeout 结束之前,继续点击 Click me 按钮,count增加到 5, 这个时候 alert 是多少?

答案:alter 是 3
你答对了么?
简单解释一下:

  1. 每次渲染相互独立,状态,时间函数都是独立的
  2. 我们再count为3的时候 触发handleAlertClick方法,这个函数记住的就是count = 3
  3. setTimeout 三秒后结束 alter 自然是3

userEffect浅析

官方文档介绍
useEffect(effectFn, deps)
使用 useEffect 我们需要忘记以前写class的时候 使用到的生命周期,函数组件跟类组件时不同的世界

  1. React会在每次渲染完后运行Effect,而依赖数组就是用来控制是否触发Effect,从而减少不必要的计算,从而优化性能
    1.1.简单说明:只要依赖数组中的每一项与上一次渲染相比没有改变,此次Effect就不会被执行
    1.2.类比:以前我们写class的时候 会使用 componentDidUpdate 来控制是否重新计算
  2. useEffect 跟class的生命周期相比,有哪些不同?
    2.1.将初次渲染(componentDidMount)、重渲染(componentDidUpdate)和销毁(componentDidUnmount)三个阶段的逻辑用一个统一的 API 去解决
    2.2.简单举例:只需要渲染一次的 我们可以直接指定 deps 为空数组 [ ]

实战小应用

1.通过Creat React App 初始化项目:npx create-react-app my-app-with-hooks
2.样式使用到了 Ant Design: yarn add antd
3.App.js源码:

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

import "./App.css";
import 'antd/dist/antd.css';
import { Button, Spin, Input, Select, Table, Rate } from 'antd';

const { Option } = Select;
const columns = [
  {
    title: '电影名',
    dataIndex: 'title',
    render: (text, record) => (
      <Button type="link" onClick={() => go(record.url)}>{record.title}</Button>
    ),
  },
  {
    title: '海报',
    dataIndex: 'url',
    render: (text, record) => (
      <img src={record.cover} style={{height: 190}} alt=""/>
    ),
  },
  {
    title: '星级',
    dataIndex: 'rate',
    render: (text, record) => {
      return <Rate disabled defaultValue={Number(record.rate)/2}/>
    }
  },
];

const BASE_URL = "https://movie.douban.com/j";

function getFetchUrl(key) {
  return `${BASE_URL}/search_subjects?type=${key}&tag=热门&page_limit=50&page_start=0`
}

function go(url) {
  window.open(url)
}

function App() {
  const [countries, setCountries] = useState([]);
  const [key, setKey] = useState("movie");
  const [inputText, setInput] = useState("");

  useEffect(() => {
    const fetchCountries = async () => {
      const response = await fetch(getFetchUrl(key));
      const data = await response.json();
      setCountries(data.subjects);
    };
    fetchCountries();
  }, [key]); // deps 加入了 key监听, 所以每次key改变 都会调用这个 useEffect

  function handleChange(value) {
    setKey(value)
  }

  return (
    <div className='App'>
      <h1>本周热门排行</h1>
      <Input style={{ width: 180}} placeholder="请输入搜索的类型" value={inputText} onChange={e => setInput(e.target.value)} />
      <Button style={{marginLeft: 10, marginRight: 10}} type="primary" onClick={() => setKey(inputText)}>搜索</Button>

      <Select defaultValue="电影" style={{ width: 120 }} onChange={handleChange}>
      <Option value="tv">电视</Option>
      <Option value="moive">电影</Option>
      <Option value="radio" disabled>
        广播
      </Option>
    </Select>
    <Table dataSource={countries} columns={columns} rowKey={(record, index) => `complete${record.id}${index}`} style={{ marginTop: 20}}/>;
      {
        countries.length === 0 ? <Spin /> : <div/>
      }
    </div>
  );
}

export default App;

效果图:


image.png

相信大家看见代码应该都明白了,我这边还是简单说明一下
1.这里我创建了两个状态 key(用来筛选 tv 跟 movie)跟 countries (列表数据填充)
2.通过 useEffect 钩子进行数据获取,但是第二个参数 deps 这里依赖了 key, 只要当 key 改变,useEffect钩子就会重新触发
3.最后将数据 setCountries 赋值给 countries

注意事项

1.不要在循环,条件或嵌套函数中调用Hook,必须始终在React函数的顶层使用Hook。这是因为React需要利用调用顺序来正确更新相应的状态,以及调用相应的钩子函数。一旦在循环或条件分支语句中调用Hook,就容易导致调用顺序的不一致性,从而产生难以预料到的后果。
2.只能在React函数式组件或自定义Hook中使用Hook。
3.借助eslint-plugin-react-hooks的 ESLint 插件来强制执行这两条规则,违反hooks规则,会有eslint提示
4.npm install eslint-plugin-react-hooks --save-dev
5.eslint配置

 {
   "plugins": [
     // ...
     "react-hooks"
   ],
   "rules": {
     // ...
     "react-hooks/rules-of-hooks": "error", // 检查 Hook 的规则
     "react-hooks/exhaustive-deps": "warn" // 检查 effect 的依赖
   }
 }

useMemo简单实例理解:

父组件

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

import "./App.css";
import Child from './components/Child'

const BASE_URL = "https://movie.douban.com/j";

function App() {
  const [price, setPrice] = useState('价格');
  const [content,setContent] = useState('内容');
  return (
    <div className='App'>
        <button onClick={() => setPrice(price + 1)}>price</button>
        <button onClick={() => setContent(content + 2)}>content</button>
        <Child price={price} content={content}></Child>
    </div>
  );
}
export default App;

子组件

mport React, { useState, useEffect, useMemo } from "react";

function Child({ price, content }) {
    function changePrice(price) {
      console.log('price: ', price);
      return price + '元'
    }
    // const otherPrice = useMemo(() => changePrice(price), [price]) 
    const otherPrice = changePrice(price)
    return (
        <>
          <div>{otherPrice}</div>
          <div>{content}</div>
        </>
    )
  }
  export default Child;

从上述代码可以看到,子组件的price 跟 content ,触发setPrice 或者 setContent 都会触发 changePrice方法,这意味着性能损耗,做了无用功,
当我们触发 setContent 的时候 希望不会触发 changePrice方法, 这个时候 使用 useMemo 就可以完美解决了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342