直接意义——函数功能的增强:
Hooks是对利用嵌套函数以及闭包等特性,让函数具备生命周期以及异步调用等功能特点,从而让函数组件具备在运用中表现出完整的功能;
间接意义——函数组件的地位
(1)完全替代class组件: 函数功能的增强,也就意味着函数组件功能增强与class组件一样具备完全的功能
(2)提升组件的松散性:类组件是一个封闭的结构化的数据结构,所以往往大量的生命周期回调以及函数,会耦合很多自身结构变量的访问,而函数组件则是清一色的表现了函数找那个
(3)简化了更多的api:setState变成了useState返回的赋值函数;
(4)最重要的一点,函数的数据逻辑(从请求后台、保存、刷新)可以单独抽成一个被增强的函数,而在以前数据都是保存在类中的,即使调用了外部函数只是做一个数据处理后返回到类中保存
,导致函数功能、函数数据、应用模块耦合
,而无法单独抽出一块函数功能去完整的保存了数据逻辑、数据保存、数据初始化
,这才叫纯粹的函数剥离,这样函数不再被组件私有,也不是一个传递数据的东西,而是一个独立的函数块,这个函数块可以是函数组件,也可以是自定义的Hooks
升级版本
package.json下,找到dependencies字段,然后修改以下两个版本
“react”: “^v16.8.0”,
“react-dom”: “^v16.8.0”,
重装依赖
npm install
import userFunction from "../function/userFunction"
import React,{
useState,useEffect} from "react"
function HooksDemo(){
/*
必须使用[]而不是{}
*/
const [userInfo,setUserInfo]=useState({
username:"GodSchool",
password:"GodSchool"
});
return (
<div>
<h1>HooksDemo测试</h1>
<h1>用户信息:{
userInfo.username}</h1>
<h1>用户信息:{
userInfo.password}</h1>
<button onClick={
()=>{
setUserInfo({
username:"xdoc",
password:"xdoc"
})
}}>更改用户新</button>
</div>
)
}
export default HooksDemo
定义函数块(函数Hooks)
如果你的函数是增强函数,则必须以use开头才能使用useState系列的一切api来完成独立的数据保存
import {
useState,useEffect} from "react"
/**
* @用户信息数据空间函数
* 直接开辟一个作用域空间,专门存储用户信息的值,用State做桥梁暴露引用来实现数据实时刷新
*/
function useGetUserInfo(){
/*
拿到数据源、数据刷新函数
*/
const [userInfo,setUserInfo]=useState({
username:"",
password:""
})
useEffect(()=>{
setUserInfo({
username:"Init",
password:"Init"
});
},[])
/*
对外暴露数据源、数据刷新函数
*/
return [userInfo,setUserInfo]
}
export default{
useGetUserInfo
}
定义函数块(函数组件)
引用函数块的内部数据以及回调函数
import React,{
useState,useEffect} from "react"
import userFunction from "../function/userFunction"
let userInfo;
let setUserInfo;
function HooksDemo(){
([userInfo,setUserInfo]=userFunction.useGetUserInfo());
return (
<div>
<h1>HooksDemo测试</h1>
<h1>用户信息:{
userInfo.username}</h1>
<h1>用户信息:{
userInfo.password}</h1>
<button onClick={
()=>{
setUserInfo({
username:"Xdoc",
password:"Xdoc"
});
}}>更改用户新户</button>
</div>
)
}
export default HooksDemo
主要在初始化的时候用于异步执行一些东西,比如:网络请求
useEffec 参数(1): 接收一个函数,可以用来做一些副作用比如异步请求,修改外部参数等行为
useEffec 参数(2): 而第二个参数称之为dependencies,是一个数组,如果数组中的值变化才会触发 执行
useEffect 第一个参数中的函数。返回值(如果有)则在组件销毁或者调用函数前调用。
特殊情况:useEffect 中传递了一个空数组[],这种情况下只有在组件初始化或销毁的时候才会触发
function useGetUserInfo(){
const [userInfo,setUserInfo]=useState({
username:"",
password:""
})
useEffect(()=>{
setUserInfo({
username:"Init",
password:"Init"
});
},[])
return [userInfo,setUserInfo]
}
在react完成DOM数据更新后
先调用再渲染
。而useEffect是会在整个页面渲染完才会调用
的代码
注意,函数组件每次渲染时都会重新走一遍函数整个方法
import React, {
useState, useEffect, useRef, useLayoutEffect } from "react"
import userFunction from "../function/userFunction"
function HooksDemo() {
const [username, setUsername] = useState("username");
useLayoutEffect(() => {
alert('先调用了————看视图更新没')
});
useEffect(() => {
alert('后调用了————看视图更新没')
});
return (
<div>
<h1>HooksDemo测试</h1>
<h3>{
username}</h3>
<button onClick={
() => {
setUsername(Math.random() * 1000)
}}>更改用户新户</button>
</div>
)
}
export default HooksDemo
useContext 是 React 帮你封装好的,用来处理多层级传递数据的方式
如果是以前的class标签形式,运用非常繁杂,需要嵌套大量的Consumer标签
function HeaderBar() {
return (
<CurrentUser.Consumer>
{
user =>
<Notifications.Consumer>
{
notifications =>
<header>
Welcome back, {
user.name}!
You have {
notifications.length} notifications.
</header>
}
}
</CurrentUser.Consumer>
);
}
利用Hooks语法
import React, {
useState, useEffect, useContext } from "react"
const HookRootContext = React.createContext(null);
function HookChildChild() {
/*
(1)拿到Context对象,然后就可以取出里面存入的值了
*/
const userInfo=useContext(HookRootContext)
return (
<div>
<h1>三级组件{
userInfo.username}</h1>
</div>)
}
function HookChild() {
/*
(2)拿到Context对象,然后就可以取出里面存入的值了
*/
const userInfo = useContext(HookRootContext)
return (
<div>
<h1>二级组件{
userInfo.username}</h1>
<HookChildChild></HookChildChild>
</div>)
}
function HookRoot() {
const [userInfo, setUserInfo] = useState({
"username": "GosSchoold",
"password": "GosSchoold"
})
return (<div>
<button onClick={
() => {
setUserInfo({
"username": "Xdoc",
"password": "Xdoc"
})
}}></button>
<HookRootContext.Provider value={
userInfo}>
<h1>一级组件{
userInfo.username}</h1>
<HookChild></HookChild>
</HookRootContext.Provider>
</div>)
}
export default HookRoot
快照的形式保存过去的引用,用于历史对比
每当重新渲染的时候function就会执行一次,因为他一整个都是render
不建议使用
const refs = []
function HooksDemo() {
const [username, setUsername] = useState("username");
const ref = useRef();
refs.push(ref)
return (
<div>
<h1>HooksDemo测试</h1>
<button onClick={
() => {
setUsername(Math.random()*1000)
console.log(refs)
}}>更改用户新户</button>
</div>
)
}
export default HooksDemo
通过 useImperativeHandle 用于让父组件获取子组件内的索引
根源:主要是为了解决父组件的 props或者状态改变了就会触发重渲染,导致整个函数组件重新渲染一次,不相干的子组件(但挂载了函数)也会重新渲染,显然是代价昂贵的
原理: 函数组件发生这个的原因是,函数组件内部无法挂载函数的引用值,因此当父组件引用重新渲染,则父函数的引用更新了,想改变子组件的函数也只有重新调用子组件的渲染方法重新赋值一次props,类组件中不存在这个问题是因为类是以对象形式保存了指针,而函数组件则是闭包局部变量只能用重新初始化来完成引用刷新
;
memo
避免子组件重新渲染,useCallback/useMemo都必须搭配memo去使用
const Child=memo(function (prop){
alert('子组件被渲染了')
return (
<div>
<button>调用父组件</button>
</div>
);
})
function HooksDemo() {
const [username,setUsername]=useState("GodSchool")
/*
使用useCallback包装一下我们原来的函数即可
然后利用[]决定子组件是否重新渲染,[]为空会则绑定个这个关联函数变了也永远不刷新子组件
*/
return (
<div>
<h1>HooksDemo测试:{
username}</h1>
<button onClick={
()=>{
setUsername(Math.random()*10000)
}}>改变值触发重新渲染</button>
<Child></Child>
</div>
)
}
export default HooksDemo
useCallBack
当父子组件发生关联时,即使父组件的数据没有发生更改,子数据依然会重新渲染,对于一些只是函数关联的组件来讲将会导致性能问题
错误示范
const Child=memo(function (prop){
alert('子组件被渲染了')
return (
<div>
<button onClick={
()=>{
prop.parentFunction()
}}>调用父组件</button>
</div>
);
})
function HooksDemo() {
const [username,setUsername]=useState("GodSchool")
/*
只要是关联传递就会触发重新渲染
*/
const parentFunction=()=>{
alert('父组件函数被调用了')
}
return (
<div>
<h1>HooksDemo测试:{
username}</h1>
<button onClick={
()=>{
setUsername(Math.random()*10000)
}}>改变值触发重新渲染</button>
<Child parentFunction={
parentFunction}></Child>
</div>
)
}
export default HooksDemo
正确示范
import React, {
useState,useCallback, useEffect,memo} from "react"
const Child=memo(function (prop){
alert('子组件被渲染了')
return (
<div>
<button onClick={
()=>{
prop.parentFunction()
}}>调用父组件</button>
</div>
);
})
function HooksDemo() {
const [username,setUsername]=useState("GodSchool")
/*
使用useCallback包装一下我们原来的函数即可
然后利用[]决定子组件是否重新渲染,[]为空会则绑定个这个关联函数变了也永远不刷新子组件
*/
const parentFunction=useCallback(()=>{
alert('父组件函数被调用了')
},[])
return (
<div>
<h1>HooksDemo测试:{
username}</h1>
<button onClick={
()=>{
setUsername(Math.random()*10000)
}}>改变值触发重新渲染</button>
<Child parentFunction={
parentFunction}></Child>
</div>
)
}
export default HooksDemo
useMemo
主要用于决定数据是否继续向下流动重复啊重新渲染
function ParentComp () {
const [ name, setName ] = useState('hi~')
const [ age, setAge ] = useState(20)
/*
比较name和age数组里的值如果新旧比较发生改变则重复info的向下传递重新渲染
*/
const info = useMemo(() => ({
name, age }), [name, age]) // 包一层
return (
<div>
<button onClick={
increment}>点击次数:{
count}</button>
<ChildComp info={
info} />
</div>
);
}
可以看看链接:https://www.jianshu.com/p/014ee0ebe959
import React, {
useState, useReducer } from "react"
/**
* @规范来讲这些state数据是放到一个state的集中文件中
*/
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 Child(){
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
return (
<div>
<h1>HooksRedux子组件:{
state.count}</h1>
<button onClick={
() => dispatch({
type: "increment", payload: 5 })}>+</button>
<button onClick={
() => dispatch({
type: "decrement", payload: 5 })}>-</button>
</div>
</div>
)
}
function HooksRedux() {
const [state, dispatch] = useReducer(reducer, initialState);
console.log(state)
return (
<div>
<h1>HooksRedux父组件:{
state.count}</h1>
<button onClick={
() => dispatch({
type: "increment", payload: 5 })}>+</button>
<button onClick={
() => dispatch({
type: "decrement", payload: 5 })}>-</button>
<Child></Child>
</div>
)
}
export default HooksRedux