react中Hooks的理解和用法
一、Hooks是什么?
Hook
是 React 16.8 的新增特性。它可以让你在不编写 class
的情况下使用 state
以及其他的 React
特性
至于为什么引入hook
,官方给出的动机是解决长时间使用和维护react
过程中常遇到的问题,例如:
- 难以重用和共享组件中的与状态相关的逻辑
- 逻辑复杂的组件难以开发与维护,当我们的组件需要处理多个互不相关的 local state 时,每个生命周期函数中可能会包含着各种互不相关的逻辑在里面
- 类组件中的this增加学习成本,类组件在基于现有工具的优化上存在些许问题
- 由于业务变动,函数组件不得不改为类组件等等
在以前,函数组件也被称为无状态的组件,只负责渲染的一些工作
因此,现在的函数组件也可以是有状态的组件,内部也可以维护自身的状态以及做一些逻辑方面的处理
二、有哪些?具体用法
1、useState
作用:为函数式组件提供动态属性
语法:let [默认数据变量, 修改数据的方法] = useState('默认数据')
修改 基本数据类型:修改这个数据的方法(最新的数据)
修改复杂数据类型 数组:修改数据方法(()=>{return [ ...新的数据]})
修改复杂数据类型 对象:修改数据方(()=>{...原数据state, 更新的属性数据})
// 引入useState
import { useState } from "react"; // hooks 为函数式组件提供一个属性 // 作用 => 为函数式组件提供动态数据 // 语法 => let [默认数据变量,修改这个数据的方法] = useState('默认数据') // // 1. 修改的是 基本数据类型
// function App(){
// // 这里组件有一个动态的数据
// // let salary=1800;
// // 动态的数据 =》 useState
// let [salary,setSalary]=useState(1800);
// const changeSalary=()=>{
// // 让salary => 改变值 => useState 处理的是基本数据类型
// // setSalary(最新的数据) => salary 是最新的数据 // setSalary(2000) // }
// return (
// <div>
// <h2>我的工资{salary}</h2>
// <button onClick={()=>changeSalary()}>修改数据</button>
// </div>
// );
// } // // 2.修改复杂数据类型之 数组
// function App() {
// // 动态数据 => 数组
// let [list, setList] = useState(["", "", ""]); // const addItem = () => {
// // 添加数据
// let item = "";
// setList((state) => {
// state.push(item);
// return [...state];
// });
// }; // const deleteItem =(index)=>{
// // 修改数据 list => 调用 他的修改数据方法 setList
// // console.log(list,index);
// // list.splice(index,1); // // 处理复杂的数据类型 => (处理方法)
// // 处理方法 特点
// // 第一个参数 就是默认数据
// // 这个处理方法的返回值就是 最新的数据 // setList((state)=>{
// state.splice(index,1); // 获取到原理的数据 => 根据索引删除
// console.log(state);
// return([...state]); // 改变引用地址
// })
// } // return (
// <div>
// {list.map((item, index) => {
// return <div key={index}>{item} <button onClick={()=>deleteItem(index)}>删除</button></div>;
// })}
// <button onClick={() => addItem()}>添加</button>
// </div>
// );
// } // 3 修改复杂数据类型之 对象
function App() {
// 定义一个动他的数据 小明信息
let [obj, setObj] = useState({ name: "小明", salary: 1800 }); const changeSalary=()=>{
setObj(state=>{
// 1 获取到原来的数据属性,在添加我们需要更新的属性
return {...state,salary:state.salary+200}
})
} return (
<div>
<h2>
{obj.name}的月薪{obj.salary} 每日笑哈哈
<button onClick={()=>changeSalary()}>修改数据 学习后</button>
</h2>
</div>
);
}
export default App; /*
总结 函数式组件的动态数据的处理 1 使用方式 useState
语法 let [默认数据,修改数据的方法]=useState(默认值) 1 基本数据类型 => 修改数据方法(新的数据) 2 数组 => 修改数据的方法(()=>{
retrun[...新的数据]
}) 3 对象 => 修改数据方法(处理方法中自己合并旧的数据) => 合并原理的数据 */
2、useEffect 副作用
Effect Hook 可以让你在函数组件中执行副作用操作
作用:1. 函数式组件的生命周期 2. 侦听器
函数式组件的生命周期
组件加载完毕:语法: useEffect(处理函数, 依赖项) ; 如果依赖项为空数组,useEffect( ()=>{ }, [ ]) 组件加载完毕,相当于mounted,执行一次,一般用来做dom操作,和发送请求
组件更新完毕:语法:useEffect(()=>{ }) ;如果没有依赖项,这个处理函数,只要这个函数(组件)重新执行,处理函数就会重新执行(一般不使用)
组件摧毁:语法:useEffect(()=>{return()=>{ }};这个useEffect 处理函数式的返回值,并且是处理函数的返回值 是一个方法;用于 清除这个组件中全局定义的一些方法
import { useEffect,useState } from "react";
// useEffect => 副作用 // 1 当组件的生命周期使用
// 写法 1 useEffect(()=>{}) => 1.加载完毕 2.更新完毕 3.组件摧毁 // 1 加载完毕 => dom 操作,发动网络请求 获取到后端的数据
// 2 组件更新 // 想要的组件的加载 和更新 =》 在工作中不用
// function App(){
// let [salary,setSalary]=useState(1800); // useEffect(()=>{
// console.log(document.getElementById('h2'));
// console.log('组件在浏览器上加载完毕,组件更新');
// }) // const addSaraly=()=>{
// setSalary(salary+200)
// } // return(
// <div>
// <h2 id='h2'>useEffect 作用</h2>
// <h3>{salary}</h3>
// <button onClick={()=>addSaraly()}>++</button>
// </div>
// )
// } // // 2 发现我们需要这个副作用, 我们的生命周期(组件加载完毕) 执行 mounted
// // 语法 useEffect(()=>{},依赖项)
// // // 如果这个依赖项为空的数组 useEffect(()=>{},[]) => 就相对于vue, 执行一次
// function App(){
// let [salary,setSalary]=useState(1800);
// console.log('组件created f') // 还是会重新创建组件 // useEffect(()=>{
// console.log(document.getElementById('h2'));
// console.log('组件加载完毕');
// },[]) // // useEffect(处理函数,依赖项)
// // 处理函数和依赖项之间的关系 // // 1 如果没有依赖项,这个处理函数,只要这个函数重新执行,处理函数就会重新执行 // // 2 如果有依赖项,但是这个依赖项为空数组,那么这个处理函数只会执行一次 // const addSaraly=()=>{
// setSalary(salary+200)
// } // return(
// <div>
// <h2 id='h2'>useEffect 作用</h2>
// <h3>{salary}</h3>
// <button onClick={()=>addSaraly()}>++</button>
// </div>
// )
// } // function Children(){ // return(
// <div> // </div>
// )
// } // 2 相当于 组件的摧毁
function App(){
let [salary,setSalary]=useState(1800);
console.log('组件created f') // 还是会重新创建组件
const addSaraly=()=>{
setSalary(salary+200)
} return(
<div>
<h2 id='h2'>useEffect 作用</h2>
<h3>{salary}</h3> {
salary==2000?<Children></Children>:<h4>444</h4>
}
<button onClick={()=>addSaraly()}>++</button>
</div>
)
} function Children(){
// 组件的摧毁的生命周期
// 写法 => 是这个useEffect 处理函数式的返回值,并且是处理函数的返回值 是一个方法 let timer=setInterval(()=>{
console.log(1)
},1000) useEffect(()=>{
return()=>{
console.log('组件摧毁了');
clearInterval(timer) // 清除全局
}
})
return(
<div>
Children
</div>
)
} /*
总结
1 函数式组件的生命周期 2 另外的3个 是通过 react hooks 提供 2.1 useEffect(()=>{},[]) =》 加载完毕 => 发送请求 dom操作 2.2 组件的更新 => 工作中不用 2.3 useEffect(()=>{return 方法}) =》 组件的摧毁 =》 清除这个组件全局定义的一个方法
*/ export default App;
组件中的侦听器
1. 作用:侦听器,相当于vue中的watch
语法:useEffect(()=>{},[监听的数据1,监听的数据2]) ;useEffect(()=>{},[依赖的数据源]),相当于立即执行的侦听器,监听的函数发生改变,处理函数就会改变
useEffectb本质就是一个宏任务
import { useEffect,useState } from "react"; // useEffect =》 相当于vue中的watch function App(){
let [salary,setSalary]=useState(1800);
let [name,setName]=useState('小红'); console.log('组件created f','还会不会重新创建组件?') // 还会不会重新创建组件? // watch => 立即执行
useEffect(()=>{
console.log(salary)
},[salary]) const addSaraly=()=>{
setSalary(salary+200)
} return(
<div>
<h2 id='h2'>useEffect 作用</h2>
<h3>{salary}</h3>
<button onClick={()=>addSaraly()}>++</button> <h3>{name}</h3>
<button onClick={()=>setName('小明')}>修改</button>
</div>
)
}
// 总结: useEffect(()=>{},[依赖的数据源]),相对于立即执行的侦听器 // useEffect 本质就是一个宏任务 export default App;
3、useContext和React.createContext
useContext
作用:全局提供数据,用来做全局设置
用法: useContext 必须要和 React.createContext 一起使用
创建一个全局数据:let Context=React.createContext() // 返回一个对象:有一个属性provider 是提供数据的
在父子组件中提供数据:<Context.Provider value={所提供的数据} ></Context.Provider>
在子组件中获取父组件提供的数据:let data=useContext(Context) // 相当于inject,data就是在父组件中获取到的数据
// useContext => 提供上下文 =》 本质=》全局变量
import React,{ useContext } from "react";
// 在最外层提供数据, 在里面可以获取到数据 // 业务中的使用 => 全局设置主体,字体 dengdeng // 使用:必须 提供数据provideer =》 React.createContext() let Context =React.createContext() // 返回一个对象 => 有一个属性 provider => 提供数据
console.log(Context) let theme={
background:'red'
}; function App(){ return(
// 在父级组件中提供数据
<Context.Provider value={theme}>
<div>
<Children></Children>
</div>
</Context.Provider>
)
} // 创建子组件
function Children(){
// 在子组件中需要获取到父级组件提供的数据
let datas=useContext(Context); // inject()
console.log(datas) // {background: 'red'}
return(
<div style={{background:datas.background}}>
Children
</div>
)
} // 总结 useContext =》 获取到 React.createContext() 提供的数据 export default App;
4、useReducer
用法:和useState 一样,都是用来处理动态数据
作用:就是对动态的数据的行为(这个动态数据修改的方法)进行约束
语法:语法: let [默认数据,dispatch] = useReducer(reducer,默认数)
核心:
retucer:reducer相当于vuex中的mutations,作用:定义行为(方法),修改动态数据
reducer是一个函数
这个函数有两个参数, 参数1 :数据 参数2: 行为
这个函数的返回值,就是最新的数据
只能处理同步函数
用来触发reducer行为,dispatch({actions}) => 这个actions是一个对象
import {useState, useReducer } from 'react' // useReducer // 用法 和useState 一样,都是用来处理动态数据
// 作用 => 就是对动态的数据的行为(这个动态数据修改的方法)进行约束
// 语法 => let [默认数据,触发reducer行为]=useReducer(reducer,默认数据) // reducer => 相当于vuex中 mutations
// 作用 => 定义行为(方法) => 修改动态数据 // reducer 是什么
// 1 它是一个函数
// 2 这个函数有两个参数, 参数1 就是你的数据 参数2 行为
// 3 这个函数返回值 就是你最新的数据
// 4 只能处理同步问题 // 定义reducer
function reducer(state,actions){
// 定义你处理逻辑 => add reduce
switch(actions.type){
case 'add':
console.log(state); // 默认参数
console.log(actions); // 行为
return state+actions.money
case 'reduce':
return state-200;
default :
return state;
}
} function App(){
let [money,setMoney]=useState(1800); let [moneys,dispatch]=useReducer(reducer,1000) const changeM=()=>{
setMoney('椅子')
} return(
<div>
<h2>{money}</h2>
<button onClick={()=>changeM()}>操作money</button> <h2>{moneys}</h2>
{/* 触发reducer => dispatch 触发reduce行为
dispatch(就是reducer actions) => 这个actions 是一个对象 传递对象 */}
<button onClick={()=>dispatch({type:'add',money:300})}>add</button>
</div>
)
}
export default App /*
总结: userReducer 核心 reducer => 用来定义处理动态数据的行为的 dispatch => 触发 reducer 中的行为
*/
5、useRef
作用:获取到元素的真实dom
语法:let dom=useRef(null) ; (此时dom=null);在标签元素上<div ref={dom}></div>; (dom就可以获取到真实的dom)
组件的创建流程:
组件创建完毕:初始化这个组件的属性
再将这个组件的模板语法(jsx)变为vnode(1. 如果有事件就集中处理,2. 如果又属性 就进行绑定)
再将这个组件变为真实dom
做一些dom操作
import {useRef,useEffect} from 'react' // useRef
// 作用 :获取到元素的真实dom // 在created 生命周期中进行定义useRef
// 用法 let dom=useRef(null) function App(){
let dom =useRef(null) // null
let datas=100; useEffect(()=>{
console.log(dom);// 获取到真实的dom
console.log(document.getElementById('h2s'));
},[]) return(
<div>
<h2 id='h2s' data-id={datas} ref={dom}>dom</h2>
</div>
)
} /*
这个组件在创建的流程 1 组件创建完毕 => 初始化 这个组件的属性
2 将这个组件 模板语法(jsx) => 变成vnode( 1 如果有事件 集中处理,2 有属性 进行绑定) =》 再将这个vnode 变成真实dom
3 做一些dom操作
*/ export default App;
6、useLayoutEffect
作用:
监听器:当监听的数据 发生改变之前就执行这个处理方法
生命周期:语法:和useEffect一样 useLayout(处理函数,依赖项)
组件更新之前:没有依赖项,只要组件更新,函数就会在组件更新前更新
挂载完成之前:依赖项为空数组,相当于mounted,执行一次
组件摧毁之前:useEffect(()=>return 方法) 返回的方法在组件摧毁前执行
useLayoutEffect:底层代码是 微任务
import {useEffect,useLayoutEffect,useState} from 'react' function App(){ // useLayoutEffect // 写法 和useEffect 一样的 /*
useLayoutEffect 作用
1 生命周期
浏览器加载之前 ,更新之前 , 摧毁之前 2 监听
当这个数据 发生改变之前就是执行这个处理方法
*/ let [age,setAge]=useState(100); // useEffect(()=>{
// console.log('加载完毕 useEffect');
// },[]) // useLayoutEffect(()=>{
// console.log('浏览器生成dom 之前 useEffect')
// },[]) useEffect(()=>{
console.log('加载完毕 useEffect');
},[age]) useLayoutEffect(()=>{
console.log(age)
console.log('浏览器生成dom 之前 useEffect')
},[age]) return(
<div>
{age}
<button onClick={()=>setAge(age+1)}>++</button>
</div>
)
} export default App; // useLayoutEffect =》 底层代码是 微任务
7、useMemo
作用:1. 缓存组件 2. 缓存变量
缓存组件:
语法:let 组件= React.memo(需要缓存的组件)
<组件></组件>
特点:缓存组件 如果这个组件的属性没有变化就不会创建,反之就会重新创建
react组件更新机制:组件的重新创建,只要数据发生改变,组件就会重新创建,会造成性能问题
组件的渲染:先渲染父组件,在渲染子组件
问题1:在子组件中更新数据,如果没有将子组件进行模块化划分,那么父组件也会重新创建,会导致性能问题
解决方法:
模块化开发:组件的模块化划分,将子组件进行抽离(一个功能一个模块),此时:(子组件数据更新,该子组件会重新创建,其父组件与兄弟组件不会重新创建)
问题2:如果我们在父组件中更新数据 => 会重新渲染组件 => 这个父组件的嵌套组件(子组件),也会被重新渲染 => 会导致性能问题
解决方法:
1. 缓存组件:React.mome(需要被缓存的组件); 缓存组件,不管父组件是否重新创建,这个子组件创建一次,如果子组件的数据没有改变,就不需要重新创建
// react 更新机制 => 组件重新创建
import React, { useState } from "react"; // // 组件模块化划分
// function App() {
// console.log("father 组件重新创建了"); // return (
// <div>
// {/* 模块化划分 */}
// <Children2></Children2>
// <Children1></Children1>
// </div>
// );
// } // function Children2() {
// console.log("children2222222222 组件重新创建");
// let [salary, setSalary] = useState(1800);
// const changeSalary = () => {
// setSalary(salary + 200);
// };
// return (
// <div>
// 月入{salary}
// <button onClick={() => changeSalary()}>更新数据+200</button>
// </div>
// );
// } // function Children1() {
// console.log("children111111 组件重新创建");
// return <div>children</div>;
// } function App() {
console.log("father 组件重新创建了");
let [salary, setSalary] = useState(1800);
const changeSalary = () => {
setSalary(salary + 200);
};
return (
<div>
月入{salary}
<button onClick={() => changeSalary()}>更新数据+200</button>
{/*
不管父组件是否重新创建,这个子组件创建一次,如果子组件的数据没有改变,就不需要重新创建
*/}
<Keepchildren1></Keepchildren1>
</div>
);
} /*
React.memo()=> 缓存组件
语法: React.memo(需要缓存的组件) => 缓存组件 特点:
缓存组件 如果这个组件属性值没有变化就不会创建,反之,就会重新创建
*/ function Children1() {
console.log("children111111 组件重新创建");
return (<div>children</div>)
}
let Keepchildren1=React.memo(Children1)
export default App; // 1 组件的渲染 => 先渲染父组件,再渲染子组件
// 2 如果我们在父组件中更新数据 => 会重新渲染组件 => 这个父组件的嵌套组件(子组件),也会被重新渲染 => 会导致性能问题 // 解决方法(子组件数据更新,该子组件会重新创建,其父组件与兄弟组件不会重新创建)
// 1 模块化开发 => 一个功能一个模块
缓存变量
1. 作用:缓存变量
2. 语法:let 缓存变量 = useMemo( ()=>{ return 缓存数据},[监听的数据源] ); // 当依赖的数据发生改变的时候才会更新
// react 更新机制 => 组件重新创建
import React, { useState,useMemo } from "react"; // useMemo 作用: 缓存变量
// 用法: let 缓存变量 = useMemo(()=>{},[监听的数据源]) // 当依赖的数据发生改变的时候才会更新 function App() {
console.log("father 组件重新创建了");
let [salary, setSalary] = useState(1800); // let name="张三"; // 这个数据写死 => 不用更新 let Kname=useMemo(()=>{
console.log('更新');
return '张三'; // 缓存数据 当依赖的数据发生改变的时候才会更新
},[salary]) const changeSalary = () => {
setSalary(salary + 200);
}; return (
<div>
<h2>{Kname}</h2>
月入{salary}
<button onClick={() => changeSalary()}>更新数据+200</button>
</div>
);
} export default App;
8、useCallback
作用:缓存方法
语法:let 缓存方法 = useCallback( ()=>{} , []); // 当依赖的数据发生改变的时候才会更新
import React, { useState,useMemo, useCallback } from "react"; // useCallback 作用: 缓存方法
// 用法: let 缓存方法 = useCallback(()=>{},[])
// 当依赖的数据发生改变的时候才会更新 function App() {
console.log("father 组件重新创建了");
let [salary, setSalary] = useState(1800); const getdata=()=>{
console.log(1200);
} let Keepgetdata=useCallback(()=>{
return getdata();
},[salary]) const changeSalary = () => {
setSalary(salary + 200);
}; return (
<div>
月入{salary}
<button onClick={() => changeSalary()}>更新数据+200</button>
<button onClick={()=>Keepgetdata()}>缓存方法</button>
</div>
);
} export default App;
9、自定义hooks
概念:
它是一个方法
这个方法是以use 开头的
在这个方法中可以使用 react 提供的api
react中Hooks的理解和用法的更多相关文章
- React中JSX的理解
React中JSX的理解 JSX是快速生成react元素的一种语法,实际是React.createElement(component, props, ...children)的语法糖,同时JSX也是J ...
- react中redux的理解
定义 redux可以看作是flux的进阶版,主要用于react中公共状态(数据)的管理 redux底层原理 redux有一个createStore方法,这个方法用户创建公共存储空间,createSto ...
- react中的context的基础用法
context提供了一种数据共享的机制,里面有两个关键概念——provider,consumer,下面做一些key features描述. 参考网址:https://react.docschina.o ...
- 对于react中rredux的理解
1.什么是redux? redux是一个应用数据流框架,主要作用是对于应用状态的管理 2.reducer特点 : (1)默认的state (2)state是只可读不可修改 (3)必须返回一个纯函数 3 ...
- React中ref的三种用法 可以用来获取表单中的值 这一种类似document.getXXId的方式
import React, { Component } from "react" export default class MyInput extends Component { ...
- vue中mixin的理解与用法
vue中提供了一种混合机制--mixins,用来更高效的实现组件内容的复用.最开始我一度认为这个和组件好像没啥区别..后来发现错了.下面我们来看看mixins和普通情况下引入组件有什么区别? 组件在引 ...
- react中的setState的使用和深入理解
前端框架从MVC过渡到MVVM.从DOM操作到数据驱动,一直在不断的进步着,提升着, angular中用的是watcher对象,vue是观察者模式,react就是state了,他们各有各的特点,没有好 ...
- 理解React中es6方法创建组件的this
首发于:https://mingjiezhang.github.io/(转载请说明此出处). 在JavaScript中,this对象是运行时基于函数的执行环境(也就是上下文)绑定的. 从react中的 ...
- React中Props 和 State用法
React中Props 和 State用法 1.本质 一句话概括,props 是组件对外的接口,state 是组件对内的接口.组件内可以引用其他组件,组件之间的引用形成了一个树状结构(组件树),如果下 ...
- 深入理解react中的虚拟DOM、diff算法
文章结构: React中的虚拟DOM是什么? 虚拟DOM的简单实现(diff算法) 虚拟DOM的内部工作原理 React中的虚拟DOM与Vue中的虚拟DOM比较 React中的虚拟DOM是什么? ...
随机推荐
- .NET 7 的 AOT 到底能不能扛反编译?
一:背景 1.讲故事 在B站,公众号上发了一篇 AOT 的文章后,没想到反响还是挺大的,都称赞这个东西能抗反编译,可以让破解难度极大提高,可能有很多朋友对逆向不了解,以为用 ILSpy,Reflect ...
- 初次邂逅 EasyExcel
前言 由于工作原因,有这种需求,就是把数据库中的数据导出成 Excel 表格,同时,也得支持人家用 Excel 表格导入数据到数据库.当前项目也是在用 EasyExcel,所以我不得不学啦! 以前学习 ...
- 关于Qt的QPixmap中不支持jpg文件格式的问题
问题 Qt部分版本存在不支持jpg,JPEG等图像格式的问题 qDebug()<<QImageWriter::supportedImageFormats(); 这行代码可以查看所支持的图像 ...
- PostgreSQL常用操作合辑:时间日期、系统函数、正则表达式、库表导入导出、元数据查询、自定义函数、常用案例
〇.参考地址 1.pg官方文档 http://www.postgres.cn/docs/9.6/index.html 2.腾讯云仓pg文档 https://cloud.tencent.com/docu ...
- 【离线数仓】Day04-即席查询(Ad Hoc):Presto链接不同数据源查询、Druid建多维表、Kylin使用cube快速查询
一.Presto 1.简介 概念:大数据量.秒级.分布式SQL查询engine[解析SQL但不是数据库] 架构 不同worker对应不同的数据源(各数据源有对应的connector连接适配器) 优缺点 ...
- 你的项目使用Optional了吗?
1.基本概念 java.util.Optional<T>类本质上就是一个容器,该容器的数值可以是空代表一个值不存在,也可以是非空代表一个值存在. 2.获取对象 2.1 相关方法 2.2 案 ...
- xxl-job定时调度任务Java代码分析
简介 用xxl-job做后台任务管理, 主要是快速解决定时任务的HA问题, 项目代码量不大, 功能精简, 没有特殊依赖. 因为产品中用到了这个项目, 上午花了点时间研究了一下运行机制. 把看到的记一下 ...
- GitHub上的一个Latex模板
代码下载:GitHub的项目地址或者在LATEX项目报告模板下载. 编译环境:Latex的编译器,如Ctex软件. 把源码clone或者下载到本地后,根据他的说明 如何开始 使用report.tex开 ...
- 主题 1 The Shell
主题 1 The Shell 课程概览与 shell · the missing semester of your cs education (missing-semester-cn.github.i ...
- 你的 GitHub 年度报告「GitHub 热点速览 v.22.52」
辞旧迎新的日子,又是年度报告满天飞的时候.GitHub 也不落其他平台之后,推出了用户 GitHub Contributions 报告.不知道,今年的你是不是比去年搬了更多的砖呢?在本期的 News ...