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 (
    
Your count:{count} {/* */}
) }

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

image.png

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

2.2 作用 Effect

作用(Effect)

  • 1.UI如果这样实现,它不仅仅是一个将数据映射到视图的函数。
  • 2.客观世界存在输入和输出之外的东西(改变URL,改变环境....)
UI=data->{
console.log( 'xxxx' )
return< 
... / 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 (
    
Your count:{count}
) }
  • 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 (
     {
        setTheme(theme => {//获取最新的theme作为setTheme的参数
          setTheme(theme === themes.light ? themes.dark : themes.light)
        })
      }
    }}>
      
    
  )
}
const Toolbar = () => {
  return 
}
const ThemedButton = () => {
  const context = useContext(ThemeContext)
  return (
    
  )
}

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

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 (
        
Your counter is : {counter}
) }

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 (
        

当前值:{counter}

{/*

之前的值:{prev}

*/}

之前的值:{prev.current}

) }

注释部分代码是有问题的,因为每次进入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 
data is loading
}else{ return (
    { list.map((name,i)=>{ return
  • {name}
  • }) }
) } }

Tips4:每种行为一个Hook

Tips5:不要再思考生命周期

你可能感兴趣的:(react hooks 基础篇)