React_05 Hooks

6 篇文章 0 订阅

目录

目标

一、hook

1.1、简介

1.2、使用 hook 限制

1.3、常用 hook 函数

1.3.1、useState

1.3.2、useEffect

1.3.3、useReducer

1.3.4、useContext

1.3.5、useMemo

1.3.6、useCallback

React.memo

1.3.7、useRef

1.3.8、useImperativeHandle

1.3.9、useLayoutEffect

1.3.10、react-redux-hook

1.3.11、react-router-dom-hook

1.4、自定义 hook

图例 :


跳转链接 =>  React_01 学习笔记

跳转链接 =>  React_02 学习笔记

跳转链接 =>  React_03 学习笔记

跳转链接 =>  React_04 菜谱项目


目标

  • 掌握 hook 相关函数方法
  • 能够自定义 hook 函数

、hook

1.1、简介

在 React 的世界中,有容器组件 和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用 函数,无状态组件来展示 UI,而对于 容器组件,函数组件 就显得无能为力,我们依赖于 类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行 渲染。React在 v16.8 的版本中推出了 React Hooks 新特性,Hook 是 一套工具 函数 的集合,它增强了 函数组件 的功能,hook 不等于 函数组件,所有的 hook 函数 都是以  use开头 

使用 React Hooks 相比于从前的 类组件 有以下几点好处:

  • 代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的 生命周期函数 中,容易使开发者不利于维护 和 迭代,通过 React Hooks 可以将功能代码 聚合,方便阅读 维护
  • 组件树 层级变浅,在原本的代码中,我们经常使用 HOC / render / Props 等方式来 复用组件的状态,增强功能等,无疑增加了 组件树层数 及 渲染,而在 React Hooks 中,这些功能都可以通过强大的 自定义 的 Hooks 来实现

1.2、使用 hook 限制

  • hook 只能用在 函数组件 中,class 类组件不行
  • 普通函数 不能使用 hook -- 例外:普通函数 名称以 use 开头也可以
  • 函数组件 内部的函数 也不行
  • hook 函数一定要放在 函数组件 的 第一层,别放在 if / for 中( 块级作用域 )
  • 要求 函数组件 名称必须 首字母 大写

1.3、常用 hook 函数

1.3.1、useState

保存 组件状态,功能类似于 类组件 中的 this.state 状态管理

 

1.3.2、useEffect

此 hook 可以 模拟 函数组件 的 生命周期,函数组件 对于在一些 生命周期 中操作还是无能为力,所以 React 提供了 useEffect 来帮助开发者处理 函数组件,来帮助模拟完成一部份的开发中非常常用的 生命周期 方法。常被别的称为:副作用处理函数。此函数的操作是 异步 的。

// useEffect 相当类组件中的 3 个生命周期
// componentDidMount componentDidUpdate componetWillUnMount
import React, { useState, useEffect } from 'react'

const App = () => {
  let [count, setCount] = useState(0)

  // 参数 1:回调函数
  // 如果没有第 2个参数,表示 componentDidMount componentDidUpdate
  // 如果第 2个参数 为空数组 表示 componentDidMount
  // 如果第 2个参数 不为空数组,只关注所写变量事件更新和挂载
  // 表示 componentDidMount componentDidUpdate

  useEffect(() => {
    if (count > 10) {
      document.title = count
    }
// 返回 回调函数中就是 componetWillUnMount
//  在执行下一个 effect 之前,上一个 effect 就已被清除
    return () => {
      // 在此处,就可以把 setTimout setInterval  清空
    }
  }, [count]);

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>+++</button>
    </div>
  );
}

import React, { useEffect, useState } from 'react';
// 副作用函数,它可以模拟 类组件中的 3 个生命周期 
// componentDidMount componentDidUpdate componentWillUnMount

const Child = () => {
  let [count, setCount] = useState(100)
  let [name, setName] = useState('')
  let [user, setUser] = useState({ id: 1, name: { age: 20 } })
  // 此函数的参数有 2 个
  // 第 1 个参数为 回调函数据,此回调函数中不能使用 async / await
  // 第 2 个参数为一个 数组,可选
  //-----------------------------------------------------
  // 如果只有参数 1,且参数 1 回调中没有返回一个函数则模拟的为
  // componentDidMount componentDidUpdate
  // 在没有第 2 个参数时,一般不建议进行 网络请求 或 更改数据,有可能出现死循环
  /* useEffect(() => {
    console.log('didmount和update');
  }) */
  //-----------------------------------------------------
  // 如果参数 2 为一个 空数据,则模拟 componentDidMount
  /* useEffect(() => {
    console.log('didmount');
  }, []) */
  //-----------------------------------------------------
  // 如果参数 2 不是一个 空数据
// 则监听数组中对象的变量数据变化 componentDidMount componentDidUpdate
  /* useEffect(() => {
    console.log('持载和count发生更新了');
  }, [count, user]) */
  //-----------------------------------------------------
  // 参数1中返回一个函数,此函数就是一个模拟销毁的生命周期  componentWillUnMount
  useEffect(() => {
    console.log('持载和count发生更新了');
    // 数据更新时会触发 和 组件被销毁时都会触发
    return () => {
      console.log('销毁');
    }
  }, [count])

  console.log('render');
  return (
    <div>
      <h3>Child</h3>
      <h3>count:{count} --- <button onClick={() => setCount(count + 1)}>++++</button></h3>
      <div>
        <input type="text" value={name} onChange={el => setName(el.target.value)} />
      </div>
      <hr />
      <h3 onClick={() => {
        /* user.name.age = 30
        setUser(user) */
        setUser(user => ({ ...user, name: { ...user.name, age: 30 } }))
      }}>{user.name.age}</h3>
    </div >
  )
}

const App = () => {
  let [show, setShow] = useState(true)
  return (
    <div>
      {
        show ? <Child /> : <div>abc</div>
      }
    </div>
  );
}

export default App;

1.3.3、useReducer

useReducer 这个 Hooks 在使用上几乎跟 Redux 一模一样,唯一缺少的就是无法使用 redux 提供的 中间件。

import React, { useState, useReducer } from 'react'

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

const App = () => {
  const [state, dispatch] = useReducer(reducer, initialState)

  return (
    <div>
      <h3>{state.count}</h3>
      <button onClick={() => dispatch({ type: 'increment' })}>+++</button>
      <button>---</button>
    </div>
  );
}

 

1.3.4、useContext

使用 useContext 可以方便我们获取 Context 中的数据源

import React, { useContext } from 'react'
import cxt from '../context/userContext'
const { Provider } = cxt

const App = () => {
  return (
    <div>
      <Provider value='hello'>
        <Cmp />
      </Provider>
    </div>
  )
}

function Cmp() {
  const context = useContext(cxt)
  return (
    <h1>
      {context}
    </h1>
  )
}

1.3.5、useMemo

记忆组件,可以理解为 计算属性  性能优化

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

const Memo = () => {
  const [count, setCount] = useState(1)
  const [val, setValue] = useState('')

  // 返回上一次缓存的值
  const sum = useMemo(() => {
    return count + 10
  }, [count]);

  return (
    <div>
      <h3>{count}-{val}-{sum}</h3>
      <div>
        <button onClick={() => setCount(count + 1)}>+ count</button>
        <input value={val} onChange={event => setValue(event.target.value)} />
      </div>
    </div>
  )
}

  

1.3.6、useCallback

记忆函数,它计算返回一个 缓存函数。

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

const App = () => {

  const [count, setCount] = useState(1);
  const [val, setVal] = useState('');

  const callback = useCallback(() => {
    return count
  }, [count])

  return (
    <div>
      <h1>父组件:{count}</h1>
      <Child callback={callback} />
      <div>
        <button onClick={() => setCount(count + 1)}>+count</button>
        <input value={val} onChange={event => setVal(event.target.value)} />
      </div>
    </div>
  )
}

function Child({ callback }) {
  const [count, setCount] = useState(() => callback())
  useEffect(() => {
    setCount(callback())
  }, [callback]);
  return (
    <div>
      <hr />
      <div>子组件:{count}</div>
    </div>
  )
}

React.memo

给函数组件来减少重复渲染的 顶层Api ,类似于 PureComponent

1.3.7、useRef

useRef 跟 createRef 类似,都可以用来生成对 DOM 对象的引用

useRef 默认只能在 html 标签中使用,不可以在 函数组件 中使用

import React, { useRef } from 'react'
const Ref = () => {
  const username = useRef()
  const login = () => {
    console.log(username.current.value);
  }
  return (
    <div>
      <div>
        <input type="text" ref={username} />
        <br />
        <br />
        <button onClick={login}>添加用户</button>
      </div>
    </div>
  )
}

1.3.8、useImperativeHandle

使用它可以透传 Ref ,因为 函数组件 没有 实例,所以在默认 自定义函数组件 中不能使用 ref 属性,使用为了解决此问题,react 提供了一个 hook 和一个 高阶组件 来帮助 函数组件 能够使用 ref 属性。

import React, { useRef, useEffect, useImperativeHandle, forwardRef } from "react"
function ChildInputComponent(props, useRef) {
  // const inputRef = useRef(null)
  // useImperativeHandle(ref, () => inputRef.current)
  // 穿透 ref
  useImperativeHandle(ref, () => ({id:1,name:’aaa’}))
  return <input type="text" ref={ useRef } />
}
const ChildInput = forwardRef(ChildInputComponent)

function App() {
  const inputRef = useRef(null)
  const getValue = () => {
    console.log(inputRef.current.value)
  }

  return (
    <div>
      <ChildInput ref={inputRef} />
      <hr/>
      <button onClick={getValue}>获取一下数据</button>
    </div>
  )
}

1.3.9、useLayoutEffect

大部分情况下,使用 useEffect 就可以帮我们处理组件的 副作用,但是如果想要 同步调用一些 副作用,比如对 DOM 的操作,就需要使用 useLayoutEffect ,useLayoutEffect 中的 副作用 会在 DOM 更新之后 同步执行。

const Layout = () => {
  const [value, setValue] = useState(0)
  useLayoutEffect(() => {
    const title = document.querySelector("#title")
    console.log("useLayoutEffect");
    setValue(title.innerHTML);
  })

  useEffect(() => {
    console.log("useEffect")
  })

  return (
    <div>
      <div><h1 id="title">hello</h1><h2>{value}</h2></div>
    </div>
  )
}

1.3.10、react-redux-hook

react-redux 支持了 hook 的解决方案,提供两个方法,很方便我们在项目去使用

import React from 'react'
// useDispatch   发送 指令
// useSelector   获取 数据
import { useDispatch, useSelector } from 'react-redux'

const MyRedux = () => {
  const { username, password } = useSelector(state => state.userinfo)
  const dispatch = useDispatch()

  return (
    <div>
      <h3>{username}</h3>
      <hr />
      <button onClick={
        e => {
          dispatch({
            type: 'setname',
            name: 'aaaaa'
          })
        }
      }>修改一下姓名</button>
    </div>
  )
}

1.3.11、react-router-dom-hook

react-router-dom 也提供了 hook 的 解决方案

import {useHistory,useLocation,useParams} from 'react-router-dom'


1.4、自定义 hook

定义自定义 hook 以 use 开头 + 函数名称,通过自定义 Hook ,可以将 组件 逻辑提取到 可重用 的函数 中。

import React, {useState, useEffect} from 'react'
// 是否在线
function useOnline() {
  const [online, setOnline] = useState(navigator.onLine)

  useEffect(() => {
    const handlerOnline = () => setOnline(true)
    const handlerOffline = () => setOnline(false)

    window.addEventListener('online', handlerOnline, false)
    window.addEventListener('offline', handlerOffline, false)

    return () => {
      window.removeEventListener('online', handlerOnline, false)
      window.removeEventListener('offline', handlerOffline, false)
    }
  })
  return online
}

function App() {
  const online = useOnline()
  return (
      <div>
        {
          online ?
              <h3 style={{color: 'green'}}>在线</h3>
              :
              <h3>离线了 </h3>
        }
      </div>
  )
}

图例 :


跳转链接 =>  React_01 学习笔记

跳转链接 =>  React_02 学习笔记

跳转链接 =>  React_03 学习笔记

跳转链接 =>  React_04 菜谱项目

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前言如释重负,好用的技术就应该越来越简单React Hooks 是 React 16.8 从提案转为正式加入的新特性。这个新特性是个非常棒的设计。 可以说对于React 技术栈的发展具分割线一样的意义。讲师在课程中提到:之前使用 React 作为主要的前端技术,开发一款网页游戏。在整个游戏的各个模块中,Redux ,mobx,以及蚂蚁金服的 ant-design,dva, umi 这些框架或者第三方库都有涉及使用。但是自从了解了Facebook官方提案的 Hooks 特性后,才真正觉得获得了前所未有的解脱。如果你有React开发经验,学习了解 Hooks 后,一定有一种如释重负的轻松感。React 带来了方便也带来了迷茫相信关心 React Hooks 这项新特性的童鞋,很多已经有了一定的 React 开发经验。那么你一定有所体验,React 给我们带来方便的同时,也的确和长久以来的前端开发模式有极大的不同。React 并不需要用继承,而是推荐用嵌套。React 有独特的 jsx 语法。大多数情况 jsx 都使得我们的代码更加简洁了。然而有些时候也给我们带来了一些困扰。 比如数据的传递,逻辑的复用。 react 是一种 mvvm 的设计模式,作为开发者一定要清楚,那些数据是业务数据,那些数据是UI数据。否则你的代码很有可能会陷入混乱局面。大型项目中模块化与功能解耦困难在公司项目中 App 稍大的时候,我们发现状态提升和只通过 props 进行数据传递。很多时候都很难实现我们的需求。这时无论我们是否清楚的了解,但是状态管理也就是 redux mobx 等,轻易地进入到了公司的项目中。我们经过初期的尝试发现状态管理,确实比用纯粹的 React 带来了数据传递上的方便,以及代码组织上的清晰。但前提是你看懂且理解了 redux 大神晦涩的官网文档。 本来 React 被设计用来组件化前端开发。但当我们初期使用状态管理,我们常常会过度的使用状态数据,业务逻辑和ui逻辑没有清楚的分离,最终你的应用代码结果可能是:除了少数几个组件是独立的解耦的,大多数组件都因为状态数据的共享而耦合在了一起,且他们也完全依赖状态管理框架。无法再轻松的转移复用。使用高阶组件,属性渲染,渲染回调等高级特性,确实可以帮我们解决模块或功能的解耦问题。但是这些方法,确实有点超出普通“猿类”的技能。且降低了代码的可读性,对于团队协作,这是很致命的问题。React Hooks 真正开启前端模块化的金钥匙对于以上问题,React Hooks 都有很好的解决方案,官方的设计动机就是解决这些曾经的繁琐,化繁为简。React Hooks 让我们在纯函数中就可以使用 React 的众多特性。而不必使用类。代码扁平,易读。解耦状态相关逻辑,UI逻辑和业务逻辑更好的分离。这些逻辑往往是纯函数,而以前很容易混合在类组件中。通过自定义 Hooks 我们可以把应用中“状态相关”逻辑解耦出来,独立编写到我们自己的hooks 中。从而更加易于复用和独立测试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值