(1) Comprender los ganchos de los componentes funcionales [useState, useEffect, useContext, useReducer]

1. ¿Ganchos?


Los ganchos son una característica nueva en [React 16.8]  . Le permite utilizar el estado y otras funciones de React sin escribir clases.

Los enlaces son funciones que le permiten "conectar" características como el estado de React y el ciclo de vida en los componentes de la función. Los ganchos no se pueden usar en componentes de clase; esto le permite usar React sin usar clases.


2. ¿Por qué utilizar ganchos?


1. Clases incomprensibles, los componentes deben comprender cómo funcionan JavaScript y esto, los controladores de eventos deben estar vinculados, existen diferencias entre los componentes de función pura y los componentes de clase, e incluso es necesario distinguir los escenarios de uso de los dos componentes; se puede usar Hook Puedes usar más funciones de React sin clases .

2. Es difícil reutilizar la lógica de estado entre componentes, es difícil dividir y reconstruir componentes grandes y también es difícil de probar. Los ganchos le permiten reutilizar la lógica de estado sin modificar la estructura del componente .

3. Los componentes complejos se vuelven difíciles de entender y la lógica empresarial se dispersa entre varios métodos del componente, lo que genera lógica duplicada o lógica asociada. Hook divide las partes interrelacionadas del componente en funciones más pequeñas (como configurar suscripciones o solicitar datos).
Nota: Los componentes de gancho y clase no se pueden usar al mismo tiempo , de lo contrario se producirá un error.


3. Uso del gancho

Los ganchos son funciones de JavaScript, pero existen dos reglas adicionales para usarlos:
1. Los ganchos solo se pueden llamar en el nivel más externo de la función. No lo llame en un bucle, juicio condicional o función anidada (subfunción).
2. Hook solo se puede llamar en los componentes de la función React. No llame dentro de otras funciones de JavaScript.

(1) Gancho básico

(1)gancho de estado : usegancho de estado


Para aquellos que han usado componentes de clase, creo que deben tener una profunda impresión de estado. Para algunas variables globales que deben usarse, ** En los componentes de clase, el método que usamos a menudo es this.state = {}, pero en ganchos que usamos La mejor manera es usar el gancho useState, y luego puede hacer referencia a esta variable global. Al hacer referencia, solo necesita usar su nombre de variable **,

useState gancho de estado


¿Cuándo utilizar Hook? Si estás escribiendo un componente funcional y te das cuenta de que necesitas agregarle algún estado, la antigua forma de hacerlo era convertir el resto en una clase. Ahora puede utilizar Hooks en componentes de funciones existentes .

import React, { useState } from 'react';

function Example() {
  // 声明一个叫 "count" 的 state 变量
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}


Ejemplo de clase equivalente

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>Click me</button>
      </div>
    );
  }
}


En useState(), acepta el valor inicial del estado como parámetro, que es el valor inicial del conteo en el ejemplo anterior. Devuelve una matriz, donde el primer elemento de la matriz es una variable que apunta al valor actual . del Estado. Similar a this.state, el segundo elemento es una función utilizada para actualizar el estado, similar a setState .
En el ejemplo anterior, no hay herencia de clases, ni esto, ni ciclo de vida, y el código es más conciso, este es el significado de usar ganchos;

Resumir:
 

1. 引入useState hooK: import React, { useState } from 'react';
2. 声明一个叫 "count" 的 state 变量:
  const [count, setCount] = useState(0);//useState(0),0是count的初始化值
3. 读取 State:  <p>You clicked {count} times</p>
4. 更新 State:
<button onClick={() => setCount(count + 1)}>
    Click me
  </button>

Declare múltiples variables de estado.
State Hook se puede usar varias veces en un componente:

// 声明多个 state 变量
  const [age, setAge] = useState(42);//声明age ,初始化值为42
  const [fruit, setFruit] = useState('banana');//声明fruit,初始化值为banana
  const [todos, setTodos] = useState([{ text: '学习 Hook' }]);声明todos,初始化值为{text: '学习 Hook'}


(2) Gancho de efecto: use gancho de efecto secundario de efecto

Obtener datos, configurar suscripciones y cambiar manualmente el DOM en los componentes de React son todos efectos secundarios .

Hay dos efectos secundarios comunes en los componentes de React: los que deben eliminarse y los que no.

1. Efectos que no requieren compensación

Ejecute algún código adicional después de que React actualice el DOM. Por ejemplo, enviar solicitudes de red, cambiar manualmente el DOM y registrar registros son operaciones comunes que no requieren limpieza .

 Utilice el gancho de efecto secundario hook-useEffect para actualizar el título de la página web

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Equivalente a usar clase: sin usar gancho, la operación de efecto secundario (actualización de datos) se coloca en las funciones componenteDidMount y componenteDidUpdate.

class ClassTitle extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 0,
    }
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`
  }
  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`
  }

  render() {
    return (
      <div>
        <h1>2. 没有使用hook的情况下 副作用(数据更新)操作</h1>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    )
  }
}

Resumir:

useEffect se puede utilizar para manejar mejor los efectos secundarios, como solicitudes asincrónicas, etc.; useEffect Hook se puede considerar como una combinación de componenteDidMount, componenteDidUpdate y componenteWillUnmount .

useEffect(() => {}, [matriz]);

useEffect() acepta dos parámetros: el primer parámetro es la operación asincrónica que desea realizar y el segundo parámetro es una matriz utilizada para proporcionar las dependencias de Effect. Mientras esta matriz cambie, se ejecutará useEffect(). Cuando se omite el segundo elemento, useEffect() se ejecutará cada vez que se represente el componente. Esto es similar a componenteDidMount para componentes de clase .

Ejemplo de implementación de un cambio de dependencia useEffect()

import React, { useState, useEffect } from 'react';

const AsyncCount = ({ countNum }) => {
  const [loading, setLoading] = useState(true);
  const [count, setCount] = useState(0);

  useEffect(() => {
    setLoading(true);
    setTimeout(() => {
      setLoading(false);
      setCount(countNum);
    }, 2000);
  }, [countNum]);
  return <>{loading ? <p>Loading...</p> : <p>{count}</p>}</>;
};

const TestCount = () => {
  const [count, setCount] = useState(0);
  const changeCount = (name) => {
    setCount(name);
  };
  return (
    <>
      <AsyncCount countNum={count} />
      <button
        onClick={() => {
          changeCount(count + 1);
        }}
      >
        增加
      </button>
      <button
        onClick={() => {
          changeCount(count - 1);
        }}
      >
        减少
      </button>
    </>
  );
};

export default TestCount;

En el ejemplo anterior, colocamos la operación asincrónica de recuento de procesamiento y si se debe representar la carga en el gancho AsyncCount, extraemos las operaciones complejas a través de ganchos, dividimos las partes relacionadas en el componente; 

A continuación, haremos una división más detallada y separaremos nuestro propio gancho.

const useCount = (countNum) => {
  const [loading, setLoading] = useState(true)
  const [count, setCount] = useState(0)

  useEffect(() => {
    setLoading(true)
    setTimeout(() => {
      setLoading(false)
      setCount(countNum)
    }, 2000)
  }, [countNum])
  return [loading, count]
}

const AsyncCount = ({ countNum }) => {
  const [loading, count] = useCount(countNum)
  return <>{loading ? <p>Loading...</p> : <p>{count}</p>}</>
}

const TestCount = () => {
  const [count, setCount] = useState(0)
  const changeCount = (count) => {
    setCount(count)
  }
  return (
    <>
      <AsyncCount countNum={count} />
      <button
        onClick={() => {
          changeCount(count + 1)
        }}
      >
        增加
      </button>
      <button
        onClick={() => {
          changeCount(count - 1)
        }}
      >
        减少
      </button>
    </>
  )
}

El componente AsyncCount anterior divide una vez más sus operaciones de efectos secundarios; en este componente, solo nos centramos en los resultados de representación. useCount acepta un número y devuelve una matriz. La matriz incluye el estado y el recuento. Cuando usemos useCount, se basará en Devolvemos diferentes estados dependiendo de los parámetros que pasemos; 

2. Efectos que deben eliminarse

Solo necesita borrarse en componenteWillUnmount, por ejemplo, usamos setInterval para actualizar la hora actual.

class FriendStatus extends React.Component{
    constructor(props){
        super(props);
        this.state = { nowTime: null};
        this.timer = null;
    }
    componentDidMount(){
        this.timer = setInterval(() => {
            this.setState({
                nowTime: new Date()
            })
        }, 1000)
    } 
    componentWillUnmount(){
        if (this.timer !== null) {
            clearInterval(timer);
        }
    }
    render(){
        let time = this.state.nowTime;
        return(
           <div>{time.toString()}</div>
        )
    }
}

Utilice el gancho de la siguiente manera:

import React, { useState, useEffect } from 'react';
 
function FriendStatus(props) {
  const [nowTime, setNowTime] = useState(new Date());
 
  useEffect(() => {    
    let timer = setInterval(() => {
        setNowTime(new Date())
    }, 1000)
    return () => {   // 返回一个清理函数
      clearInterval(timer);
    };
  }, []);
  return(<div>{nowTime.toString()}</div>)
}

efecto mecanismo de limpieza opcional. Cada efecto puede devolver una función de limpieza. Esto reúne la lógica para agregar y eliminar suscripciones. Todos ellos son parte del efecto

3. Optimización del rendimiento omitiendo el efecto

Realizar una limpieza o ejecutar efectos después de cada renderizado causará problemas de rendimiento. En la clase, agregamos la lógica de comparación entre prevProps y prevState en componenteDidUpdate para resolver el problema;

componenteDidUpdate(prevProps, prevState){     if(this.state.count!== prevState.count){         document.title = `点击了{this.state.count}次`     } }



Usar efectos en ganchos

 useEffect(() => {     console.log('--useEffect' fue ejecutado)     document.title = `Se hizo clic {count} veces`; }, [count]); // Actualizado cuando la representación inicial y el recuento cambian


Si el segundo parámetro de matriz es [], el efecto se ejecutará una vez durante el renderizado inicial y una vez más incluirá la función de borrado Efecto (puede reemplazar [count] en el código anterior con [] para realizar pruebas); las dos situaciones se detallan a continuación .Ejecutar código 

// 带清除函数即为useEffect的第一个参数(函数)中再返回一个函数
// 不带清除函数+第二个参数为[];
// --> 整个生命周期只执行一次,相当于componentDidMount;
useEffect(() => {
    console.log('执行了--useEffect');
    document.title = `点击了${count}次`;
}, []);
 
// 带清除函数+第二个参数为[];
// --> 整个生命周期中执行了两次,相当于componentDidMount和componentWillUnmount;
useEffect(() => {
    console.log('执行了--useEffect');
    document.title = `点击了${count}次`; 
    return () => {  // 相当于componentWillUnmount;
        console.log('执行了--清除函数');
        document.title = "";   
    }
}, [])

Si no agrega el segundo parámetro de matriz, el efecto se ejecutará no solo una vez para la representación inicial, sino también para cada actualización.

(3) gancho de estado compartido useContext()

Si necesita compartir el estado entre componentes profundos, puede usar useContext(). El contexto proporciona una manera de compartir accesorios entre componentes sin tener que pasar accesorios explícitamente a través del árbol de componentes capa por capa ; el gancho useContext es más conveniente que usar el contexto en el componente de clase original ;

Recibe un objeto de contexto creado por React.createContext() (definido aquí como Mycontext) y devuelve el valor vinculado al valor del atributo de contexto; obtiene los accesorios pasados ​​por el <Mycontext.Provider value=""> más cercano a través del valor del valor useContext; como se muestra en el siguiente uso

// Mycontext es el valor de retorno de React.createContext
const Mycontext = React.createContext();
<Mycontext.provider value={}>
    ...
</Mycontext.provider>
------------ - -------------
const value = useContext(Mycontext);//El componente que llama a useContext siempre se volverá a representar cuando cambie el valor del contexto.

 La cúpula es la siguiente:


import React, { useContext } from 'react';

//1.接收一个 context 对象并返回该 context 的当前值
const themes = {
    light: {
      foreground: '#000000',
      background: '#eeeeee',
    },
    dark: {
      foreground: '#ffffff',
      background: '#222222',
    },
  }
// 当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。
const ThemeContext = React.createContext(themes.light)
function Toolbar(props) {
    return (
      <div>
        <ThemedButton />
      </div>
    )
  }
  
  function ThemedButton() {
    //调用了useContext的组件总会在context值变化时重新渲染。
    const theme = useContext(ThemeContext)
    return (
      <button style={
   
   { background: theme.background, color: theme.foreground }}>
        I am styled by theme context!
      </button>
    )
  }
function ExampleHook() {
  return (
    <div>
      <ThemeContext.Provider value={themes.dark}>
        <Toolbar />
      </ThemeContext.Provider>
    </div>
  )
}
export default ExampleHook

(2) Ganchos adicionales

(1)useReducer

En algunos escenarios, useReducer es  useState más aplicable, por ejemplo, la lógica de estado es compleja y contiene múltiples subvalores, o el siguiente estado depende del estado anterior, etc. En pocas palabras, a menudo se usa para gestionar algunos estados complejos y es adecuado para escenarios con muchas acciones .

gramática:

const [estado, despacho] = useReducer(reductor, inicialArg, init);

parámetro:

Parámetro 1: useReducer acepta una función reductora y el reductor acepta dos parámetros, uno es estado y el otro es acción.
Parámetro dos: useReducer acepta un estado inicial, inicialArg. Pasar el estado inicial como segundo parámetro para usarReducer es el método más simple.
Parámetro tres: useReducer acepta una función init e inicializa el estado a través de init (initialArg). Esto crea el estado inicial de forma perezosa.

valor de retorno:

返回Una función de estado statedispathmétodo, el estado devuelve el valor en el estado y el despacho es una función que puede publicar eventos para actualizar el estado.

Se puede ver que hay 两种diferentes formas 初始化de usarReducer state: una es pasar directamente el estado inicial en el segundo parámetro, la otra es crear el estado inicial a través de init() en el tercer parámetro

A两个参数时

import React, { useReducer } from 'react';
const init = {
 count: 0
};
 
function reducer(state, action){
  switch(action.type){
    case 'add': 
     return {count: state.count + 1};
    case 'minus':
      return {count: state.count - 1};
    default: throw new Error();
  }
}
 
function TestReducer(){
  const [state, dispatch] = useReducer(reducer, init);
  return (
    <div>
      count: {state.count}
      <ul>
        <li><button onClick={() => dispatch({type: 'add'})}>+</button></li>
        <li><button onClick={() => dispatch({type: 'minus'})}>-</button></li>
      </ul>
    </div>
  )
}
 
export default TestReducer;

三个参数

import React ,{useReducer}from 'react';

const App2 = () => {
    const initialState = { name:'张三' , location : '北京' , count : 0 }
    const init = (v) => {
        console.log('v2',Object.prototype.toString.call(v)==='[object Object]') //判断是否是对象
        console.log('v',v)
        return v
    }
    const reducer = ( state , action ) => {

        switch(action.type){
            case 'add':
                return {
                    ...state,
                    count : state.count + 1
                }
            case 'minus':
                return {
                    ...state,
                    count:state.count - 1
                }
                
            case 'reset':
                return init(action.payLoad)
            default : 
                throw Error
        }
        
        
    }
    const [state, dispatch] = useReducer(reducer, initialState , init)
    return (
        console.log('state',state),
        <div>
            <div>
                <button onClick={()=>dispatch({type:'add'})}>加号</button>
            </div>
                现在的值:{state.count}
            <div>
                <button onClick={()=>dispatch({type:'minus'})}>减号</button>
            </div>
            <div>
                <button onClick={()=>dispatch({type:'reset', payLoad : initialState})}>重置</button>
            </div>
        </div>
    );
};

export default App2;

(2)useReducer + useContext 的组合

En algunos escenarios, useReducer es más adecuado que useState , por ejemplo, la lógica del estado es compleja y contiene múltiples subvalores , o el siguiente estado depende del estado anterior, etc. Además, usar useReducer también puede optimizar el rendimiento de los componentes que desencadenan actualizaciones profundas, porque puede pasar el envío a componentes secundarios en lugar de funciones de devolución de llamada .

A la mayoría de las personas no les gusta pasar manualmente devoluciones de llamada a través de cada nivel del árbol de componentes. Aunque esta forma de escribir es más explícita, se siente como un intrincado trabajo de plomería.

En árboles de componentes grandes, nuestra alternativa recomendada es usar useReducer para pasar una función de distribución a través del contexto.

import React, { useState, useEffect, useContext, useReducer } from 'react'
// 1. 声明一个变量
const init = {
  count: 1,
}
// 2. 创建需要共享的context
const ThemeContext = React.createContext(null)

function reducer(state, action) {
  switch (action.type) {
    case 'add':
      return { count: state.count + 1 }
    case 'minus':
      return { count: state.count - 1 }
    default:
      throw new Error()
  }
}

// Toolbar 组件并不需要透传 ThemeContext
function Toolbar(props) {
  return (
    <div
      style={
   
   {
        backgroundColor: '#faad14',
        padding: '10px',
      }}
    >
      <p>这是子组件</p>
      <ThemedButton />
    </div>
  )
}

function ThemedButton(props) {
  // 5.使用共享 Context:如果我们想要执行一个 action,我们可以从 context 中获取 dispatch。
  const { state, dispatch } = useContext(ThemeContext)
  const handleClick = () => {
    dispatch({ type: 'add' })
  }
  return (
    <div
      style={
   
   {
        backgroundColor: '#f00',
        padding: '10px',
      }}
    >
      <p>这是孙组件:{state.count}</p>
      <button onClick={handleClick}>点击</button>
    </div>
  )
}

export default function HookreduceContext() {
  //4. 创建useReducer 对象
  const [state, dispatch] = useReducer(reducer, init)
  // 3.使用 Provider 提供 ThemeContext 的值,Provider所包含的子树都可以直接访问ThemeContext的值
  return (
    <div
      style={
   
   {
        backgroundColor: '#13ce66',
        padding: '10px',
        width: '200px',
        margin: 'auto',
        marginTop: '20px',
      }}
    >
      <ThemeContext.Provider value={
   
   { state, dispatch }}>
        <p>这是父组件</p>
        <Toolbar />
      </ThemeContext.Provider>
    </div>
  )
}

useReducer + useContext 的组合, capaz de 子组件poder 获取到上级组件传递过来的状态y poder进行修改

 Resumir:

1.useContext crea un estado global sin pasar el estado capa por capa.
2. useReducer crea un reductor y actualiza el estado según los diferentes envíos.
3. Agregue el estado dondequiera que esté escrito el código, sin interrumpir el hilo de pensamiento y saltar a redux para escribirlo.
4. Separe el estado global para evitar que el proyecto crezca y dificulte la gestión del árbol de estado de Redux.

Supongo que te gusta

Origin blog.csdn.net/gao_xu_520/article/details/125386551
Recomendado
Clasificación