1、setstate Hook
import React from 'react'
function Demo() {
console.log('Demo');
const [count,setCount] = React.useState(0);
const [name,setName] = React.useState('Tom');
function add() {
setCount(count+1)
}
function changeName() {
setName('jack')
}
return(
<div>
<h2>{count}</h2>
<button onClick = {add}>点我+1</button>
<h2>{name}</h2>
<button onClick = {changeName}>点我改名</button>
</div>
)
}
export default Demo
2、Effect Hook
1)Effect Hook可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
2)React中副作用操作:
发ajax请求数据获取
设置订阅/启动定时器
3)语法和说明:
useEffect(() =>{
//在此可以执行任何带副作用的操作
return () =>{ //在组件卸载欠执行
//再次做一些收尾工作,比如清除定时器/取消订阅等
}
},[stateValue]) //如果指定的是[],回调函数只会在第一次render()后执行
4)可以把useEffect Hook看作如下三个函数的组合
componentDidMount()
componentDidUpdate()
componentWillUnmount()
import React, { useEffect, useState } from 'react'
import ReactDOM from 'react-dom'
function Demo() {
const [count,setCount] = useState(0)
useEffect(() => {
let timer = setInterval(() => {
setCount(count => count+1)
}, 1000)
//这里返回的函数相当于componentWillUnmount钩子
return ()=>{clearInterval(timer)}
}, [])
//这里的[]为监视[]里的变量,如果里面传个变量[count],那么每当count的值发生改变时就会调用 useEffect()
function unmount(){
ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}
return(
<div>
<h2>{count}</h2>
<button onClick = {unmount}>点我卸载组件</button>
</div>
)
}
export default Demo
总结:
1、当没有[]时,useEffect相当于componentDidmount和componentDidUpdate钩子的组合
当[]里的值为空时,相当于componentDidmount钩子
当[]里传值时,相当于componentDidmount钩子+[变量]的监视
2、componentWillUnmount相当于useEffect的返回函数:return ()=>{clearInterval(timer)}
3、ref Hook
Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
function Demo() {
//定义一个ref
const myRef = React.useRef()
function show(){
//对被ref传值的dom节点进行操作
alert(myRef.current.value)
}
return(
<div>
//将ref值传给这个dom节点
<input type="text" ref = {myRef}/>
<br />
<button onClick = {show}>点我获取数据</button>
</div>
)
}
4、Fragment
因为每个子组件的dom节点外面都套了一层div,所以为了避免每个子组件在父组件进行展示时,有多个div,可以使用Fragment
return(
<Fragment>
<input type="text" ref = {myRef}/>
<br />
<button onClick = {show}>点我获取数据</button>
</Fragment>
)
还可以直接写一个空标签
return(
<>
<input type="text" ref = {myRef}/>
<br />
<button onClick = {show}>点我获取数据</button>
</>
)
唯一不同的是Fragment可以加key值,空标签加不了
5、Context
import React from 'react'
//先定义
const MyContext = React.createContext()
function A() {
const person = {name:'zyf',age:18}
return(
<>
//通过这个标签进行包裹,声明把value的值传给<B/>及其后代组件,传多个值可以传一个对象, 如<MyContext.Provider value = {{name,age}}>
<MyContext.Provider value = {person}>
<B/>
</MyContext.Provider>
</>
)
}
function B() {
return(
<>
<C></C>
</>
)
}
function C() {
const person = {name:'zyf',age:'male'}
return(
<>
<h2>
//后代要读取值的话需要用MyContext里的Consumer方法
<MyContext.Consumer>
{
value =>{
return`名字是:${person.name},年龄是:${person.age}`
}
}
</MyContext.Consumer>
</h2>
</>
)
}
export default A
类式组件也可以用上面的方法进行传值,另外类式组件比函数式组件多一种写法:
import React, { Component } from 'react'
const MyContext = React.createContext()
export default class A extends Component {
state = {name:'zyf',age:10}
render(){
return(
<>
<MyContext.Provider value = {this.state}>
<B/>
</MyContext.Provider>
</>
)
}
}
class B extends Component {
render(){
return(
<>
<C></C>
</>
)
}
}
class C extends Component {
//调用值时需要先声明接收
static contextType = MyContext
render(){
return(
<> //接收完毕后用context关键字来进行接收值
<h3>年龄是{this.context.age}</h3>
</>
)
}
}
6、useReducer
import { useReducer } from 'react';
export default function HomePage() {
// 根据action的属性值判断来用那个方法
const [count,setDispatch] = useReducer((state,action)=>{
switch (action.type){
case 'Add':
return state +1;
case 'Sub':
return state-1
}
// state的默认值为1
},1)
const addHandler = ()=>{
// 调用一次就使得state值加1
setDispatch({type:'Add'})
}
const subHandler = ()=>{
// 调用一次就使得state值减1
setDispatch({type:'Sub'})
}
return (
<div>
<button onClick={addHandler}>添加</button>
<span>{count}</span>
<button onClick={subHandler}>减少</button>
</div>
);
}
7、React.memo
父组件index.tsx
import { useState } from 'react';
import DocsPage from './docs';
export default function HomePage() {
const [state1,setState1] = useState(true)
const [state2,setState2] = useState(true)
const change1 = ()=>{
setState1(!state1)
}
const change2 = ()=>{
setState2(!state2)
}
console.log("父组件发生渲染");
return (
<div>
<button onClick={change1}>按钮1</button>
<button onClick={change2}>按钮2</button>
<DocsPage value = {state1}/>
</div>
);
}
子组件docs.tsx
import React from "react";
const DocsPage = (props) => {
console.log('子组件发生渲染');
return (
<div>
{props.value}
</div>
);
};
export default DocsPage ;
此时无论点击按钮1还是按钮2子组件和父组件都会发生渲染
自组件用React.memo后
docs.tsx
import React from "react";
const DocsPage = (props) => {
console.log('子组件发生渲染');
return (
<div>
{props.value}
</div>
);
};
export default React.memo(DocsPage) ;
点击按钮1时,父组件和子组件都发生渲染,点击按钮2时只有父组件发生渲染,所以可以得出结论为只有当props发生改变时子组件才会发生渲染
8、useCallback
index.tsx
import { useState } from 'react';
import DocsPage from './docs';
export default function HomePage() {
const [state,setState] = useState(true)
const [num,setNum] = useState(0)
const test = ()=>{
setState(state)
}
const onAdd = ()=>{
setNum(num+1)
}
console.log("父组件发生渲染");
return (
<div>
<button onClick={onAdd}>父组件onAdd</button>
<button onClick={test}>test</button>
<DocsPage value = {onAdd}/>
</div>
);
}
docs.tsx
import React from "react";
const DocsPage = (props) => {
console.log('子组件发生渲染');
return (
<div>
<button onClick={props.onAdd}>子组件onAdd</button>
</div>
);
};
export default React.memo(DocsPage) ;
此时会发现点击test按钮,子组件也会发生渲染,React.memo失效了,这是因为,当点击test按钮时,onAdd按钮也被重新赋值,且为引用值,因引用地址发生了变化,所以子组件也被渲染了
解决方法
index.tsx
import { useCallback, useState } from 'react';
import DocsPage from './docs';
export default function HomePage() {
const [state,setState] = useState(true)
const [num,setNum] = useState(0)
const test = ()=>{
setState(!state)
}
const onAdd = useCallback(()=>{
setNum(num+1)
},[])
console.log("父组件发生渲染");
return (
<div>
<button onClick={onAdd}>父组件onAdd</button>
<button onClick={test}>test</button>
{num}
<DocsPage value = {onAdd}/>
</div>
);
}
此时点击test按钮就会发现子组件不会随着发生渲染了,因为,useCallback和useEffect一样,当第二个参数为空数组时,只有在初次渲染时才会被重新赋值,但同时引来新的一个问题,就是点击onAdd按钮时,值只会更新一次,这是因为,它的作用域没有发生变化,在它的作用域内,num就一直是为1,所以点击时就只有第一次有效
此时需要在第二个参数里添加依赖变量num,当num值发生改变时,onAdd函数被重新赋值,作用域也发生改变,num值也发生了变化
9、useMemo
useMemo的用法与useCallbackl类似,唯一的区别就是useMemo接受的是值,useCallBack返回的是函数
import { useMemo, useState } from 'react';
const func = (param:number)=>{
console.log('hello');
return param
}
export default function HomePage() {
const [state,setState] = useState(1)
const count = useMemo(()=>{
return func(1)+state
},[state])
const onAdd = ()=>{
setState(state+1)
}
return (
<div>
<span>{count}</span>
<button onClick={onAdd}>父组件onAdd</button>
</div>
);
}
如若不用useMemo包裹起来,每次点击test按钮,都会执行func里的函数,用了useMemo如若不加监听,state值就会一直为初始值1,和之前说过的useCallback一样