react之react Hooks
函数组件,没有 class 组件中的 componentDidMount、componentDidUpdate 等生命周期方法,也没有 State,但这些可以通过 React Hook 实现。
React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来
React Hooks优点
- 代码可读性更强:通过 React Hooks 可以将功能代码聚合,方便阅读维护
- 组件树层级变浅:在原本的代码中,我们经常使用 HOC/render props 等方式来复用组件的状态,增强功能等,无疑增加了组件树层数及渲染,而在 React Hooks 中,这些功能都可以通过强大的自定义的 Hooks 来实现
九种常用的钩子
- useState:保存组件状态
- useEffect: 处理副作用
- useContext: 减少组件层级
- useReducer:类似于redux,通信
- useCallback: 记忆函数
- useMemo: 记忆组件
- useRef: 保存引用值
- useImperativeHandle: 透传 Ref
- useLayoutEffect: 同步执行副作用
1、useState保存组件状态
用来代替:state,setState
若使用对象做 State,useState 更新时会直接替换掉它的值,而不像 setState 一样把更新的字段合并进对象中。推荐将 State 对象分成多个 State 变量。
类组件案例
在类组件中,我们使用 this.state 来保存组件状态,并对其修改触发组件重新渲染。
import React from "react";
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      name: "alife"
    };
  }
  render() {
    const { count } = this.state;
    return (
      <div>
        Count: {count}
        <button onClick={() => this.setState({ count: count + 1 })}>+</button>
        <button onClick={() => this.setState({ count: count - 1 })}>-</button>
      </div>
    );
  }
}
函数组件useState
在函数组件中,由于没有 this 这个黑魔法,React 通过 useState 来帮我们保存组件的状态。
- 通过传入 useState 参数后返回一个带有默认状态和改变状态函数的数组[obj, setObject] (名称是自定义的可修改[count, setCount]...)。
- 通过传入 新状态给函数 来改变原本的状态值。
import React, { useState } from "react";
function App() {
  const [obj, setObject] = useState({
    count: 0,
    name: "alife"
  });
    let [count, setCount] = useState(0)
  return (
    <div className="App">
      Count: {obj.count}
      <button onClick={() => setObject({ ...obj, count: obj.count + 1 })}>+</button>
      <button onClick={() => setObject({ ...obj, count: obj.count - 1 })}>-</button>
    </div>
  );
}
注意: useState 不帮助你处理状态,相较于 setState 非覆盖式更新状态,useState 覆盖式更新状态,需要开发者自己处理逻辑。(代码如上)
2、useEffect 处理副作用
用来代替:componentDidMount、componentDidUpdate和componentWillUnmount的组合体。
默认情况下,useEffect会在第一次渲染之后和每次更新之后执行,每次运行useEffect时,DOM 已经更新完毕。
为了控制useEffect的执行时机与次数,可以使用第二个可选参数施加控制。
类组件案例
在例子中,组件每隔一秒更新组件状态,并且每次触发更新都会触发 document.title 的更新(副作用),而在组件卸载时修改 document.title(类似于清除)
从例子中可以看到,一些重复的功能开发者需要在 componentDidMount 和 componentDidUpdate 重复编写,而如果使用 useEffect 则完全不一样。
import React, { Component } from "react";
class App extends Component {
  state = {
    count: 1
  };
  componentDidMount() {
    const { count } = this.state;
    document.title = "componentDidMount" + count;
    this.timer = setInterval(() => {
      this.setState(({ count }) => ({
        count: count + 1
      }));
    }, 1000);
  }
  componentDidUpdate() {
    const { count } = this.state;
    document.title = "componentDidMount" + count;
  }
  componentWillUnmount() {
    document.title = "componentWillUnmount";
    clearInterval(this.timer);
  }
  render() {
    const { count } = this.state;
    return (
      <div>
        Count:{count}
        <button onClick={() => clearInterval(this.timer)}>clear</button>
      </div>
    );
  }
}
useEffect
参数1:接收一个函数,可以用来做一些副作用比如异步请求,修改外部参数等行为。
参数2:称之为dependencies,是一个数组,如果数组中的值变化才会触发 执行useEffect 第一个参数中的函数。返回值(如果有)则在组件销毁或者调用函数前调用
- 比如第一个 useEffect 中,理解起来就是一旦 count 值发生改变,则修改 documen.title 值;
 
- 而第二个 useEffect 中传递了一个空数组[],这种情况下只有在组件初始化或销毁的时候才会触发,用来代替 componentDidMount 和 componentWillUnmount,慎用;
 
- 还有另外一个情况,就是不传递第二个参数,也就是useEffect只接收了第一个函数参数,代表不监听任何参数变化。每次渲染DOM之后,都会执行useEffect中的函数。
 
import React, { useState, useEffect } from "react";
let timer = null;
function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = "componentDidMount" + count;
  },[count]);
  useEffect(() => {
    timer = setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);
    // 一定注意下这个顺序:
    // 告诉react在下次重新渲染组件之后,同时是下次执行上面setInterval之前调用
    return () => {
      document.title = "componentWillUnmount";
      clearInterval(timer);
    };
  }, []);
  return (
    <div>
      Count: {count}
      <button onClick={() => clearInterval(timer)}>clear</button>
    </div>
  );
}
3、useContext 减少组件层级
用来代替:Provider, Consumer
处理多层级传递数据的方式,在以前组件树种,跨层级祖先组件想要给孙子组件传递数据的时候,除了一层层 props 往下透传之外,我们还可以使用 React Context API 来帮我们做这件事
类组件案例
const { Provider, Consumer } = React.createContext(null);
function Bar() {
  return <Consumer>{color => <div>{color}</div>}</Consumer>;
}
function Foo() {
  return <Bar />;
}
function App() {
  return (
    <Provider value={"grey"}>
      <Foo />
    </Provider>
  );
}
useContext使用的方法
- 要先创建createContex
 使用createContext创建并初始化import { createContext } from 'react'
 const C = createContext(null);
 
- Provider 指定使用的范围
 在圈定的范围内,传入读操作和写操作对象,然后可以使用上下文<C.Provider value={{n,setN}}>
 这是爷爷
 <Baba></Baba>
 </C.Provider>
 
- 最后使用useContext
 使用useContext接受上下文,因为传入的是对象,则接受的也应该是对象const {n,setN} = useContext(C);
 
import React, { createContext, useContext, useReducer, useState } from 'react'
import ReactDOM from 'react-dom'
// 创造一个上下文
const C = createContext(null);
function App(){
  const [n,setN] = useState(0)
  return(
    // 指定上下文使用范围,使用provider,并传入读数据和写入据
    <C.Provider value={{n,setN}}>
      这是爷爷
      <Baba></Baba>
    </C.Provider>
  )
}
function Baba(){
  return(
    <div>
      这是爸爸
      <Child></Child>
    </div>
  )
}
function Child(){
  // 使用上下文,因为传入的是对象,则接受也应该是对象
  const {n,setN} = useContext(C)
  const add=()=>{
    setN(n=>n+1)
  };
  return(
    <div>
      这是儿子:n:{n}
      <button onClick={add}>+1</button>
    </div>
  )
}
ReactDOM.render(<App />,document.getElementById('root'));
4、useReducer 数据交互
用来代替:Redux/React-Redux
唯一缺少的就是无法使用 redux 提供的中间件
import React, { useReducer } from "react";
const initialState = {
  count: 0
};
function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + action.payload };
    case "decrement":
      return { count: state.count - action.payload };
    default:
      throw new Error();
  }
}
function App() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "increment", payload: 5 })}>
        +
      </button>
      <button onClick={() => dispatch({ type: "decrement", payload: 5 })}>
        -
      </button>
    </>
  );
}
5、useCallback 记忆函数
减少重复渲染
老规矩,第二个参数传入一个数组,数组中的每一项一旦值或者引用发生改变,useCallback 就会重新返回一个新的记忆函数提供给后面进行渲染。
这样只要子组件继承了 PureComponent 或者使用 React.memo 就可以有效避免不必要的 VDOM 渲染。
import React, { useState, useCallback, memo } from 'react'
const Child = memo(function(props) {
  console.log('child run...')
  return (
    <>
      <h1>hello</h1>
      <button onClick={props.onAdd}>add</button>
    </>
  )
})
export default function UseCallback() {
  console.log('parent run...')
  let [ count, setCount ] = useState(0)
  const handleAdd = useCallback(
    () => {
      console.log('added.')
    },
    [],
  )
  return (
    <div>
      <div>{count}</div>
      <Child onAdd={handleAdd}></Child>
      <button onClick={() => setCount(100)}>change count</button>
    </div>
  )
}
6、useMemo 记忆组件
useCallback 的功能完全可以由 useMemo 所取代,如果你想通过使用 useMemo 返回一个记忆函数也是完全可以的。
区别:useCallback 不会执行第一个参数函数,而是将它返回给你,而 useMemo 会执行第一个函数并且将函数执行结果返回给你
    function App() {     const memoizedHandleClick = useMemo(() => () => {         console.log('Click happened')     }, []); // 空数组代表无论什么情况下该函数都不会发生改变     return <SomeComponent onClick={memoizedHandleClick}>Click Me</SomeComponent>;     }    
- useCallback: 常用记忆事件函数,生成记忆后的事件函数并传递给子组件使用。
- useMemo: 更适合经过函数计算得到一个确定的值,比如记忆组件。
function Parent({ a, b }) {
 const child1 = useMemo(() => () => <Child1 a={a} />, [a]);
 const child2 = useMemo(() => () => <Child2 b={b} />, [b]);
 return (
 <>
 {child1}
 {child2}
 </>
 )
 }
 
7、useRef 保存引用值
用来代替:createRef
Ref
React提供了一个属性ref,用于表示对组价实例的引用,其实就是ReactDOM.render()返回的组件实例:
- ReactDOM.render()渲染组件时返回的是组件实例;
- 渲染dom元素时,返回是具体的dom节点。
ref可以挂载到组件上也可以是dom元素上;
- 挂到组件(class声明的组件)上的ref表示对组件实例的引用。不能在函数式组件上使用 ref 属性,因为它们没有实例:
- 挂载到dom元素上时表示具体的dom元素节点。
useRef()
useRef这个hooks函数,除了传统的用法之外,它还可以“跨渲染周期”保存数据。
- 可以通过 ref.current 值访问组件或真实的 DOM 节点,从而可以对 DOM 进行一些操作,比如监听事件等等
export default function App() {
 const count = useRef(0)
 <!-- count.current存储状态值 -->
 const handleClick = (num) => {
 count.current += num
 setTimeout(() => {
 console.log("count: " + count.current);
 }, 3000)
 } return (
 <div>
 <p>You clicked {count.current} times</p>
 <button onClick={() => handleClick(1)}>增加 count</button>
 <button onClick={() => handleClick(-1)}>减少 count</button>
 </div>
 );
 }
8、useImperativeHandle 透传 Ref
通过 useImperativeHandle 用于让父组件获取子组件内的索引
useImperativeHandle 应当与 forwardRef 一起使用
useImperativeHandle(ref, createHandle, [deps])
- ref:定义 current 对象的 ref createHandle:一个函数,返回值是一个对象,即这个 ref 的 current
- [deps]:即依赖列表,当监听的依赖发生变化,useImperativeHandle 才会重新将子组件的实例属性输出到父组件
- 注意:ref 的 current 属性上,如果为空数组,则不会重新输出。
import React, { useRef, useEffect, useImperativeHandle, forwardRef } from "react";
function ChildInputComponent(props, ref) {
  const inputRef = useRef(null);
  useImperativeHandle(ref, () => inputRef.current);
  return <input type="text" name="child input" ref={inputRef} />;
}
const ChildInput = forwardRef(ChildInputComponent);
function App() {
  const inputRef = useRef(null);
  useEffect(() => {
    inputRef.current.focus();
  }, []);
  return (
    <div>
      <ChildInput ref={inputRef} />
    </div>
  );
}
export default App
9、useLayoutEffect 同步执行副作用
大部分情况下,使用 useEffect 就可以帮我们处理组件的副作用,但是如果想要同步调用一些副作用,比如对 DOM 的操作,就需要使用 useLayoutEffect,useLayoutEffect 中的副作用会在 DOM 更新之后同步执行。
(1) useEffect和useLayoutEffect有什么区别?
简单来说就是调用时机不同
- useLayoutEffect:和原来- componentDidMount&- componentDidUpdate一致,在react完成DOM更新后马上同步调用的代码,会阻塞页面渲染。
- useEffect:是会在整个页面渲染完才会调用的代码。
- 官方建议优先使用useEffect
在实际使用时如果想避免页面抖动(在useEffect里修改DOM很有可能出现)的话,可以把需要操作DOM的代码放在useLayoutEffect里。关于使用useEffect导致页面抖动。
不过useLayoutEffect在服务端渲染时会出现一个warning,要消除的话得用useEffect代替或者推迟渲染时机。
import React from 'react'
import { useState, useEffect } from 'react'
export default function (props) {
  let [data, setData] = useState({count: 0})
  function loadData() {
    return fetch('http://localhost:8080/api/movies/list')
      .then(response => response.json())
      .then(result => {
        return result
      })
  }
  useEffect(() => {
    console.log('effect')
  }, [data])
  useEffect(() => {
    console.log('mounted.')
    ;(async ()=>{
      let result = await loadData()
      console.log(result)
    })()
    // return () => {
    //   console.log('unmout')
    // }
  }, [])
  return (
    <>
      <div>{data.count}</div>
      <button onClick={() => setData(data => ({count: data.count + 1}))}>click</button>
    </>
  )
}
react之react Hooks的更多相关文章
- React 16.x &  Hooks
		React 16.x & Hooks Hooks https://reactjs.org/docs/hooks-intro.html https://reactjs.org/docs/hook ... 
- 10分钟了解 react 引入的 Hooks
		"大家好,我是谷阿莫,今天要将的是一个...",哈哈哈,看到这个题我就想到这个开头.最近react 官方在 2018 ReactConf 大会上宣布 React v16.7.0-a ... 
- [React] Fix "React Error: Rendered fewer hooks than expected"
		In this lesson we'll see an interesting situation where we're actually calling a function component ... 
- React实战教程之从零开始手把手教你使用 React 最新特性Hooks API 打造一款计算机知识测验App
		项目演示地址 项目演示地址 项目代码结构 前言 React 框架的优雅不言而喻,组件化的编程思想使得React框架开发的项目代码简洁,易懂,但早期 React 类组件的写法略显繁琐.React Hoo ... 
- React学习笔记-1-什么是react,react环境搭建以及第一个react实例
		什么是react?react的官方网站:https://facebook.github.io/react/下图这个就是就是react的标志,非常巧合的是他和我们的github的编辑器Atom非常相似. ... 
- 小谈React、React Native、React Web
		React有三个东西,React JS 前端Web框架,React Native 移动终端Hybrid框架,React Web是一个源码转换工具(React Native 转 Web,并之所以特别提出 ... 
- React的React Native
		React的React Native React无疑是今年最火的前端框架,github上的star直逼30,000,基于React的React Native的star也直逼20,000.有了React ... 
- React Navigation & React Native & React Native Navigation
		React Navigation & React Native & React Native Navigation React Navigation https://facebook. ... 
- 重谈react优势——react技术栈回顾
		react刚刚推出的时候,讲react优势搜索结果是几十页. 现在,react已经慢慢退火,该用用react技术栈的已经使用上,填过多少坑,加过多少班,血泪控诉也不下千文. 今天,再谈一遍react优 ... 
随机推荐
- 实战经验分享:使用 PyO3 来构建你的 Python 模块
			PyO3 主要用于创建原生 Python 的扩展模块.PyO3 还支持从 Rust 二进制文件运行 Python 代码并与之交互,可以实现 rust 与 Python 代码共存.在一些对性能要求较高的 ... 
- LOJ6469 Magic(trie)
			纪念我菜的真实的一场模拟赛 首先看到这个题目,一开始就很毒瘤.一定是没有办法直接做的. 我们考虑转化问题 假设,我们选择枚举\(x\),其中\(x\)是\(10\)的若干次方,那么我们只需要求有多少对 ... 
- C#开发BIMFACE系列49 Web网页中加载模型与图纸的技术方案
			BIMFACE二次开发系列目录 [已更新最新开发文章,点击查看详细] 在BIMFACE二次系列博客中详细介绍了服务器端API的调用方式,如下列表 C#开发BIMFACE系列1 BIMFAC ... 
- Python中pymongo find 遍历数据导致timeout
			背景 在读取大约200W左右的数据的时候采用游标形式进行数据遍历时,超过10分钟就报错 timeout 原因 pymongo游标会在10分钟之后被关闭 解决方案 db.find({}, no_curs ... 
- UI BLOCK自定义枚举控件的宽度
			三步: 1.修改PresentationStyle属性为Radio Box 2.修改NumberOfColumns属性为指定的宽度(显示字符的个数) 3.将PresentationStyle属性改回O ... 
- Apache Zookeeper Java客户端Curator使用及权限模式详解
			这篇文章是让大家了解Zookeeper基于Java客户端Curator的基本操作,以及如何使用Zookeeper解决实际问题. Zookeeper基于Java访问 针对zookeeper,比较常用的J ... 
- .Net Core中使用ElasticSearch(一)
			一.安装配置 在官网下载Es,注意版本号,不同大版本号之间差异很大.我安装的是7.14.0版本 1.1 安装成服务 cmd 进入bin目录下执行 elasticsearch-service.bat i ... 
- vue3.x新特性之setup函数,看完就会用了
			最近有小伙伴跟我聊起setup函数,因为习惯了vue2.x的写法导致了,setup用起来觉得奇奇怪怪的,在一些api混编的情况下,代码变得更加混乱了,个人觉得在工程化思想比较强的团队中使用setup确 ... 
- js--数组的 fill() 填充方法详解
			前言 我们知道了很多了初始化数组的方法,但是初始化数组之后,数组中的每一项元素默认为 empty 空位占位,如何对数组这些空位添加默认的元素,ES6提供了 fill() 方法实现这一操作.本文总结数组 ... 
- 修改openstack镜像--支持root密码登陆
			一.前言 从openstack官方下载的云镜像一般都是普通用户密钥登陆,比如centos镜像的普通用户为centos,ubuntu镜像的普通用户为ubuntu,虽然密钥登陆系统相比密码登陆来说比较方便 ... 
