// 这里可以任意命名,因为返回的是数组,数组解构
const [state, setState] = useState(initialState);
1、每次渲染都是独立的闭包
每一次渲染都有它自己的 Props 、 State 和 有它自己的事件处理函数
当点击更新状态的时候,函数组件都会重新被调用,那么每次渲染都是独立的,取到的值不会受后面操作的影响
function Counter2(){
let [number,setNumber] = useState(0);
function alertNumber(){
setTimeout(()=>{
// alert 只能获取到点击按钮时的那个状态
alert(number);
},3000);
}
return (
<>
{number}
>
)
}
2、函数式更新
如果新的 state 需要通过使用先前的 state 计算得出,那么可以将回调函数当做参数传递给 setState。该回调函数将接收先前的 state,并返回一个更新后的值。
function Counter(){
let [number,setNumber] = useState(0);
function lazy(){
setTimeout(() => {
// setNumber(number+1);
// 这样每次执行时都会去获取一遍 state,而不是使用点击触发时的那个 state
setNumber(number=>number+1);
}, 3000);
}
return (
<>
{number}
>
)
}
3、惰性初始化 state
initialState 参数只会在组件的初始化渲染中起作用,后续渲染时会被忽略
如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用
function Counter5(props){
console.log('Counter5 render');
// 这个函数只在初始渲染时执行一次,后续更新状态重新渲染组件时,该函数就不会再被调用
function getInitState(){
return {number:props.number};
}
let [counter,setCounter] = useState(getInitState);
return (
<>
{counter.number}
>
)
}
1 、Object.is (浅比较)
function Counter(){
const [counter,setCounter] = useState({name:'计数器',number:0});
console.log('render Counter')
// 如果你修改状态的时候,传的状态值没有变化,则不重新渲染
return (
<>
{counter.name}:{counter.number}
>
)
}
2 、减少渲染次数
import React,{useState,memo,useMemo,useCallback} from 'react';
let oldData,oldAddClick;
export default function Counter2(){
console.log('Counter render');
const [name,setName]= useState('计数器');
const [number,setNumber] = useState(0);
// 父组件更新时,这里的变量和函数每次都会重新创建,那么子组件接受到的属性每次都会认为是新的
// 所以子组件也会随之更新,这时候可以用到 useMemo
// 有没有后面的依赖项数组很重要,否则还是会重新渲染
// 如果后面的依赖项数组没有值的话,即使父组件的 number 值改变了,子组件也不会去更新
//const data = useMemo(()=>({number}),[]);
const data = useMemo(()=>({number}),[number]);
console.log('data===oldData ',data===oldData);
oldData = data;
// 有没有后面的依赖项数组很重要,否则还是会重新渲染
const addClick = useCallback(()=>{
setNumber(number+1);
},[number]);
console.log('addClick===oldAddClick ',addClick===oldAddClick);
oldAddClick=addClick;
return (
<>
setName(e.target.value)}/>
>
)
}
const initialState = 0;
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {number: state.number + 1};
case 'decrement':
return {number: state.number - 1};
default:
throw new Error();
}
}
function init(initialState){
return {number:initialState};
}
function Counter(){
const [state, dispatch] = useReducer(reducer, initialState,init);
return (
<>
Count: {state.number}
>
)
}
import React,{useState,memo,useMemo,useCallback,useReducer,createContext,useContext} from 'react';
import ReactDOM from 'react-dom';
const initialState = 0;
function reducer(state=initialState,action){
switch(action.type){
case 'ADD':
return {number:state.number+1};
default:
break;
}
}
const CounterContext = createContext();
// 第一种获取 CounterContext 方法:不使用 hook
function SubCounter_one(){
return (
{
value=>(
<>
{value.state.number}
>
)
}
)
}
// 第二种获取 CounterContext 方法:使用 hook ,更简洁
function SubCounter(){
const {state, dispatch} = useContext(CounterContext);
return (
<>
{state.number}
>
)
}
/* class SubCounter extends React.Component{
static contextTypes = CounterContext
this.context = {state, dispatch}
} */
function Counter(){
const [state, dispatch] = useReducer((reducer), initialState, ()=>({number:initialState}));
return (
)
}
ReactDOM.render( , document.getElementById('root'));
import React,{Component,useState,useEffect} from 'react';
import ReactDOM from 'react-dom';
function Counter(){
const [number,setNumber] = useState(0);
// useEffect里面的这个函数会在第一次渲染之后和更新完成后执行
// 相当于 componentDidMount 和 componentDidUpdate:
useEffect(() => {
document.title = `你点击了${number}次`;
});
return (
<>
{number}
>
)
}
ReactDOM.render( , document.getElementById('root'));
function Counter(){
let [number,setNumber] = useState(0);
let [text,setText] = useState('');
useEffect(()=>{
console.log('开启一个新的定时器')
let $timer = setInterval(()=>{
setNumber(number=>number+1);
},1000);
// useEffect 如果返回一个函数的话,该函数会在组件卸载和更新时调用
// useEffect 在执行副作用函数之前,会先调用上一次返回的函数
// 如果要清除副作用,要么返回一个清除副作用的函数
return ()=>{
console.log('destroy effect');
clearInterval($timer);
}
});
// },[]);//要么在这里传入一个空的依赖项数组,这样就不会去重复执行
return (
<>
setText(event.target.value)}/>
{number}
>
)
}
useCallback:接收一个内联回调函数参数和一个依赖项数组(子组件依赖父组件的状态,即子组件会使用到父组件的值) 该回调函数仅在某个依赖项改变时才会更新
useMemo:把创建函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算
import React,{useState,memo,useMemo,useCallback} from 'react';
let oldData,oldAddClick;
export default function Counter2(){
console.log('Counter render');
const [name,setName]= useState('计数器');
const [number,setNumber] = useState(0);
// 父组件更新时,这里的变量和函数每次都会重新创建,那么子组件接受到的属性每次都会认为是新的
// 所以子组件也会随之更新,这时候可以用到 useMemo
// 有没有后面的依赖项数组很重要,否则还是会重新渲染
// 如果后面的依赖项数组没有值的话,即使父组件的 number 值改变了,子组件也不会去更新
//const data = useMemo(()=>({number}),[]);
const data = useMemo(()=>({number}),[number]);
console.log('data===oldData ',data===oldData);
oldData = data;
// 有没有后面的依赖项数组很重要,否则还是会重新渲染
const addClick = useCallback(()=>{
setNumber(number+1);
},[number]);
console.log('addClick===oldAddClick ',addClick===oldAddClick);
oldAddClick=addClick;
return (
<>
setName(e.target.value)}/>
>
)
}
function LayoutEffect() {
const [color, setColor] = useState('red');
useLayoutEffect(() => {
alert(color);
});
useEffect(() => {
console.log('color', color);
});
return (
<>
颜色
>
);
}
const refContainer = useRef(initialValue);
import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
function Parent() {
let [number, setNumber] = useState(0);
return (
<>
>
)
}
let input;
function Child() {
const inputRef = useRef();
console.log('input===inputRef', input === inputRef);
input = inputRef;
function getFocus() {
inputRef.current.focus();
}
return (
<>
>
)
}
ReactDOM.render( , document.getElementById('root'));
import React, { useLayoutEffect, useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
function useNumber(){
let [number,setNumber] = useState(0);
useEffect(()=>{
setInterval(()=>{
setNumber(number=>number+1);
},1000);
},[]);
return [number,setNumber];
}
// 每个组件调用同一个 hook,只是复用 hook 的状态逻辑,并不会共用一个状态
function Counter1(){
let [number,setNumber] = useNumber();
return (
)
}
function Counter2(){
let [number,setNumber] = useNumber();
return (
)
}
ReactDOM.render(<> >, document.getElementById('root'));