react hooks 基础篇

0.课前提问

1.什么是ui(核心主旨)
2.状态是数据吗
3.什么是状态

0.1 什么是ui(核心主旨)

UI= f(data),准确吗?
函数f将数据(data)映射到用户界面(ui)
例如:[good1,good2,good3] =》goodList

0.2 状态是数据吗?不是

0.3 状态(state)是什么?

状态有一个隐含的意思,就是存在改变状态的行为(behavior) 。
例如:点赞数(likes-状态)隐含了增加一个赞(addLike-行为);而这个
行为(点赞)只有在某中上下文(context)中才会存在。
(ps:对上下文的理解可以是我们共同知道的一些东西,比如说我们都是中国人,能够听得懂中文,生活在中国很多年是我们的上下文)

我们需要,重新再思考UI是什么

1.hooks的基本概念

1.1 什么是React Hooks

  • React 16.8的新增特性,可以让你在不写class的情况下使用state
    等react特性。
  • hooks是对函数式组件的极大加强

1.2 如何描述UI

前言
看上去UI= f(data),但是实际上还有很多的内置函数定时器,页面跳转这些

1.旧的思考方式

UI从数据开始,数据产生视图,用户点击视图产生消息,消息触发重算机制来重算数据,产生循环


QQ图片20200621190143.png

像vue,react,angular的模型都是这样的单向数据流模型

  • 1.消息->重算 抽象成行为


    QQ图片20200621190239.png

消息和重算是视图和数据背后的行为

  • 2.行为
    行为中也有异步行为


    QQ图片20200621190314.png
  • 3.数据拆分:不变的是属性,变化的是状态


    QQ图片20200621190416.png

    以不变的属性传进视图,再用行为驱动状态的变化

  • 4.状态映射了行为,因此行为可以封装在状态内


    QQ图片20200621190433.png

比如说我想点赞,点赞是一个行为,行为引申出来是用户看到的视图,视图背后是他的状态。简化一下就是ui是状态和视图的循环。状态分为同步行为和异步行为也就是同步状态异步状态。
(异步状态最典型的就是promise。promise是用来描述一个即将发生的值也可以是未来的值。他背后代表的值,不是一个函数,不是用来调用的,就是一个值)
状态也可以代表未来的一个状态,所以状态可以含有行为

比如说:流是什么?字符流,文件流,数据流。流代表一个未来的值。代表未来可能发生的所以值的集合。流看上去是一个状态或者是一个数据,但其实背后有一个时间的关系。

所以我们可以说状态是可以发生变化的。状态完全可以包含它的行为而不让视图去感知。视图只要感知状态而无需感知行为。

  • 5.让作用可以监听所有的一切


    QQ图片20200621190441.png

    发现在原来的模型上,光有状态和视图,还不够。因为还差作用,比如说用户console.log应该怎么算?不算在视图和状态里(UI描述),那应该怎么算?

比如跳转功能算什么?不算状态,因为没有和数据和行为对上关系。什么也不改变就直接跳走了。所以抽象出另一个东西叫作用,而window.location.href 就算是一种作用

作用:状态和视图之外的。作用需要上下文。这个上下文就是作用需要理解有哪些状态。

比如说根据状态做出的跳转。但这不是状态背后的行为。他是个作用,因为用户没有登录是一个布尔值,他并不是去改变这个布尔值,而是直接跳转了,从而产生了UI的变化。

  • 6.这些概念如何和视图关联?


    QQ图片20200621190446.png

    我们看到的就是一个视图,而状态,作用,上下文这些都可以看做一种行为
    举个例子,用户想要跳转到一个URL,那么这个跳转URL本质上是一个作用,但这个作用也是一种行为。

状态,比如说点赞,点赞增加的是一个数字,这个数字是可以和视图绑定上的,但是本身点赞也是一种行为

所以无论是状态,作用还是之后的一些概念,这些行为都是和视图关联上的。而这种关联我们要尽量做到松散的耦合,便于复用。因为如果都写在视图里就难以进行复用了。

这种关联的关系,我们称作hooks。

1.3 什么是Hooks

  • 重新定义UI界面是什么
    主要包括这些要素:state hook,effect hook,context hook

  • 函数V =f(props,state) V视图
    UI = V usehook1(),usehook2()...

一个视图使用了第一个hook,使用了第二个hook
比如说点赞的视图,点赞的hook提供了点赞的能力和点赞的状态

2.三个基础的hook

2.1 状态

在某个上下文中(用户界面)数据和改变数据的行为

const [count,setCount] = useState(0)
count是状态
setCount是行为
useState是hooks API
React hooks帮助我们将数据和行为绑定

import React,{ useState} from 'react';

function useCount(){
//useCount就是一个描述,描述状态背后的行为
  const [count, setCount] = useState(0)
  return [count,()=>{
    setCount(count+1)
  }]
}
export default function Example(){
  // const [count, setCount] = useState(0);
  const [count, addCount] = useCount(0);
//定义状态和关联的行为,他俩是一组

  return (
    <div>
      Your count:{count}
      {/* <button onClick={ () => setCount(count+1)}>Add</button> */}
      <button onClick={ () => addCount()}>Add</button>
    </div>
  )
}

click触发setCount行为,行为触发状态的变化,最终导致UI的变化.也可以自定义hook

image.png

改变状态的值需要依赖他的行为,数据和行为是强关联,而数据和UI是弱关联,这样就达成了和UI的解耦
状态一定要和他的行为封装在一起,这样才能形成一个独立的行为模块

2.2 作用 Effect

作用(Effect)

  • 1.UI如果这样实现,它不仅仅是一个将数据映射到视图的函数。
  • 2.客观世界存在输入和输出之外的东西(改变URL,改变环境....)
UI=data->{
console.log( 'xxxx' )
return< <div> ... / div>
}
QQ图片20200621202648.png

虽然Example是函数调用,但是要把它理解成一个描述,描述的时候需要一个作用(useInterval)。每次render,作用就执行一次

useEffect理解为依赖xx变化的作用
//读作:依赖count变化的作用
useEffect( log. bind(null,count), [count]);
//读作:依赖[变化]变化的作用
useEffect( log.bind(null,count)) ;
只要function渲染有任何的状态变化,那就需要执行一次effect
依赖空只变化一次,因为空不会变化

2.2.1 React Hooks如何描述作用(Effect)

  • 1.客观世界有url、计时器、logger.....我们做不到完美而纯净(pure functional)的视图渲染。

  • 2.相同(或类似)的作用如何进行复用,React团队提出了这个方案。
    useEffect ( someEffect )
    function someEffect() {
    console.log (...)
    }

封装定时器组件

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

function useInterval(callback, time){
  useEffect( ()=>{
    const I= setInterval(callback, time);
    return ()=>{
//在return里销毁定时器
      clearInterval();
    }
  },[])
//useEffect只执行一次,因为依赖是空,空不会改变
}
export default function Example(){
  const [count, setCount] = useState(0);

  useInterval(()=>{
    //setCount(count + 1)
   setCount(count=>count + 1)
  },1000)

  return (
    <div>
      Your count:{count}
    </div>
  )
}

  • useInterval使用效果(effect).当发生依赖的时候,函数
    (()=》{} )才被注入到效果里。
  • 使用效果的时候用的是最初版本的callback,count=0,count+1=1,count被函数缓存下来了
    导致结果打印1 1 1 1 ..
    count值没有改变,但计时器一直在执行
    因为每次执行example都会创造一个新的函数(useInterval里面),每次使用最初版本count=0,count被函数缓存下来了
  • 解决方法是设置成函数 setCount(count=>count + 1)
    设置成函数,react会帮助你取到最新的count

2.3 上下文

上下文就是你理解事物需要的背景知识。


QQ图片20200621204400.png
  • UI产生的过程中,能够从context中获取信息(知识)
  • UI更像一个人而不是机械的结构。
UI => (data) => {
const {userType} = useContext (UserTypeContext)
switch (userType) {
...
不同的渲染逻辑
}
}

改变背景色的案例

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

const themes = {
  light: {
    foreground: "#000000",
    background: '#eeeeee'
  },
  dark: {
    foreground: "#ffffff",
    background: '#222222'
  }
}
//定义创建上下文
const ThemeContext = React.createContext({
  theme: themes.light,
  toggle: () => { }
})
export default () => {
  // const [count, setCount] = useState(0);
  const [theme, setTheme] = useState(themes.light);
//xxx.Provider,xxx作为上下文提供者
  return (
    <ThemeContext.Provider value={{//下发上下文
      theme,
      toggle: () => {
        setTheme(theme => {//获取最新的theme作为setTheme的参数
          setTheme(theme === themes.light ? themes.dark : themes.light)
        })
      }
    }}>
      <Toolbar />
    </ThemeContext.Provider>
  )
}
const Toolbar = () => {
  return <ThemedButton />
}
const ThemedButton = () => {
  const context = useContext(ThemeContext)
  return (
    <button
      style={{
        fontSize: "32px",
        color: context.theme.foreground,
        backgroundColor: context.theme.background
      }}
      onClick={
        () => { context.toggle(); console.log(context.theme)}
      }>Click me!
    </button>
  )
}

联系生活,把一个人放在一个地方,他要做事情就得依赖他所处的环境

2.4 Reducer

QQ图片20200621210234.png

(设计模式)提供一种抽象状态行为的通用封装(action),以及计算过程的抽象方案(reducer)
reducer其实是一个函数,把action映射成为状态.把很多action映射为一个状态.通过reducer实现一个状态背后有多个行为

import React, { useReducer } from "react"

function reducer(state, action) {
    switch (action.type) {
        case "add":
            return state + 1;
        case "sub":
            return state - 1;
        default:
            throw "...";
    }
}

export default function Counter() {
    const [counter, dispatch] = useReducer(reducer,0)
    return (
        <div>
            Your counter is : {counter}
            <button onClick={ ()=> dispatch({ type: "add" })}>+</button>
            <button onClick={ ()=> dispatch({ type: "sub" })}>-</button>
        </div>
    )
}

2.6 引用行为 ref

  • 引用React管理以外的对象

  • 需要在React之外做一些事情:例如:focus、 媒体对象操作等

  • 通常搭配useEffect

  • 附带作用:方便地保存值

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

export default function UseRefExample() {
    const [counter, setCounter] = useState(0);
   // let prev = null
   const prev = useRef(null);
//null代表reference初始值
    return (
        <div>
            <p>当前值:{counter}</p>
            {/* <p>之前的值:{prev}</p> */}
            <p>之前的值:{prev.current}</p>
            <button onClick={()=>{
               // prev = counter
                prev.current = counter
                setCounter(x=>x+1)
            }}>
                Click me to add
            </button>
            <button
            onClick={()=>{
                //prev = counter
                prev.current = counter //实现了保留上一次的值
                setCounter(x=>x-1)
            }}>
                Click me to sub
            </button>
        </div>
    )
}

注释部分代码是有问题的,因为每次进入useRefExample,prev都被置为null,但是如果放到函数外面会造成污染。因为如果有其他函数使用prev会影响到当前函数里的prev

2.5.2 缓存

为什么要缓存? .

  • V = f(state, props) useHooks...
  • 想在f中new Object(); 只创建一次
  • 一些复杂的计算只有在状态改变后才做
  • 缓存一个函数(useCallback)
  • 缓存一个值(useMemo)

3.使用hook是的建议

Tips1:使用React.memo减少重绘次数

Tips2: hooks同步问题

使用作用时要避免副作用,比如说把定时器回收

Tips3:可以构造自己的hooks封装行为

//需求:模拟获取接口数据,并显示在页面上
//1.写一个函数getPerson模拟调用接口函数,sleep函数模拟延迟效果
//2.写一个函数usePerson封装状态和作用(使用useState,useEffect)
//3.使用自定义hooks,根据是否有数据在页面上显示不同的效果
import React, { useState,useEffect } from 'react'

function sleep(time){
    return new Promise((reslove)=>{
        setTimeout(() => {
            reslove() 
        }, time);
    })
}

async function getPerson(){
    await sleep(200)
    return ['1','2','3']
}

function usePerson(){
    const [list, setList] = useState(null)

    async function request(){
        const list = await  getPerson()
        return setList(list)
    }
    useEffect(request,[])
    return list
}
export default () => {
    const list = usePerson()
    if(list===null){
        return <div>data is loading</div>
    }else{
        return (
            <ul >
                {
                    list.map((name,i)=>{
                        return <li>{name}</li>
                    })
                }
            </ul>
        )
    }
    
}

Tips4:每种行为一个Hook

Tips5:不要再思考生命周期

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