Reaccionar notas de estudio-(Bilibili Li Chao)

cambio de ortografía

Antes

const divNode = <div>你好,React</div>
ReactDOM.render(divNode,document.getElementById('root'))

advertirá

Ahora

<script type="text/babel">
    const divNode = <div>你好,React</div>
    const root = ReactDOM.createRoot(document.getElementById('root'))
    root.render(divNode)
</script>

Al mismo tiempo, no se admiten objetos de renderizado.

const array = ['动感超人','西瓜超人'];//支持
    const listObj = [
        {name:'李白',sex:'男'},
        {name:'李白2',sex:'男'},
    ]
    const divNode = <div>你好,React {listObj}//不支持</div>
    const root = ReactDOM.createRoot(document.getElementById('root'))
    root.render(divNode)

introducir

import ReactDOM from "react-dom/client"

La tarjeta está empaquetada como interfaz de usuario

  • cuerpo de la etiqueta de función
  • pase el nombre de la clase
const Logs = () => {
  return (
          <Card  className="logs">
            <LogItem/>
          </Card>
      )
};

/*Card设置为其添加阴影效果*/
const Card = (props) => {
    return (
        <div className={`card ${props.className}`}>
            {props.children}
        </div>
    );
};

Encuadernación bidireccional de formularios

  • Realizado por setState y onChange,
  • No controlado no usa estado, controlado, usa estado y vincula valor
  <Card className="login-form">
            <div className='login-form_item'>
                <label >
                    <span className='label' >日期:</span>
                    <input  type='date' value={inputDate} onChange={(e) => setInputDate(e.target.value)}/>
                </label>
            </div>

            <div className='login-form_item'>
                <label>
                    <span className='label'>内容:</span>
                    <input type='text' placeholder='请输入学习内容' value={inputContent} onChange={e => setInputContent(e.target.value)}/>
                </label>
            </div>

            <div className='login-form_item'>
                <label>
                    <span className='label'>时长:</span>
                    <input type='text' placeholder='请输入学习时长' value={inputTime} onChange={e => setInputTime(e.target.value)}/>
                </label>
            </div>
            <div className='login-form_operation'>
                <button className='login-form_operation_btn'>添加计划</button>
            </div>
        </Card>

El cuadro de diálogo solo se mostrará cuando los datos sean verdaderos

No olvides los brackets

Solucionar problemas de jerarquía de diálogo

  • Apariencia: el componente es el descendiente del componente principal de forma predeterminada y se representa en la página, lo que genera problemas jerárquicos.

  • Solución

    • igual al elemento raíz
    • Utilice el portal para representar el componente en la ubicación especificada de la página web
  • Instrucciones

    • index.htmlañadir un contenedor
    <div id='portal-root '></div>
    
    • Obtenga el contenedor DOM donde se usa
    const protalDOM = document.getElement('portal-root');
    
    const Backdrop = () => {
    
    	return ReactDOM.createProtal(jsx内容,目标位置)
    }
    
    
       const divPortal = document.getElementById('portal-root');
        return ReactDOM.createPortal((
            <div className='selfMask'>
                {props.children}
            </div>
        ),divPortal)
    

Agregar función de filtro

  • Presta atención al problema de las cadenas, porque aquí se usa el signo igual, por lo que se convierte en un número cuando se pasa
    const handleChange = (e) => {
        /*传递值*/
        console.log(e.target.value)
        props.onChange(+e.target.value);
    }
    return (
        <div>
            <select value={props.defaultValue} onChange={handleChange}>
                <option value={2022}>2022</option>
                <option value={2021}>2021</option>
                <option value={2020}>2020</option>
            </select>
        </div>
    );

crear-reaccionar-app

  • React.StrictModeefecto
    • Use el modo estricto para renderizar componentes

Estilos en línea y estilos en línea

  • Se puede hacer referencia a esto, establecer dinámicamente el estilo de acuerdo con el valor del estado

  • import "./App.css"¿Contaminará globalmente? , la introducción es global, y otros componentes también pueden verlo
    • import "./App.css"Es una importación global, que se puede ver en App.js, y otros componentes también pueden usar el nombre de clase en App.css

Css modular (para evitar la contaminación global)

  1. Crear un xxx.module.css
.p1{
    color:red
}
.app_wrapper{
    color:red
}
.app_wrapper_info {
    background-color: green;
}
.app_wrapper_name {
    background-color: blue;
}

  1. Introducido en el componente
//一般引入的名称为classes
import classes from "./App.module.css";
  1. Establecer la clase por clases
注意,最好不要出现除字母,数字,下划线以外的类名
比如出现app-wrapper_name就不要出现'-'了
  • Si incluye etiquetas a través de .module.css, como establecer directamente el div en marrón, seguirá afectando a los componentes globales.

Configuración de la ventana gráfica

Contexto

  • Para evitar pasar componentes capa por capa, puede usar context

Un método de comunicación de componentes, a menudo utilizado para la comunicación [祖组件]entre [后代组件]

manejar el tamaño de fuente

  • Es normal en un teléfono pequeño, pero si cambias a un teléfono con una pantalla grande, habrá problemas con la fuente.

  • razón

    • problema de proporción de píxeles
    • Así que no configure la fuente en px, solo configúrela en rem
  • Pero si usa el complemento de conversión y no establece el tamaño de fuente en el marcador de posición de la entrada, esto sucederá. Por lo tanto, la mejor manera de evitarlo es establecer el tamaño de fuente dondequiera que aparezca la fuente.

público/img y src/activo

  • A los que están bajo público se puede acceder por el servidor, es decir, hay direcciones especiales para ver esto
  • El servidor no puede acceder a src/asset, es decir, no hay ninguna entrada para ver esto

Evite el burbujeo de eventos

  • Agregue lo siguiente al div que el div no quiere cancelar
<div className={classes.detail} onClick={e => e.stopPropagation()}>

capa de máscara

  • Si la capa de máscara necesita recibir todos los parámetros, se puede escribir así
  <div {...props} className={`${classes.selfMask} ${props.className}`} >
            {children}
        </div>
  • Pero ojo, {...props}escríbelo al frente, si lo escribes al reverso, sobreescribirá los classNameatributos ya escritos

Hay muchos casos de burbujeo en el proyecto.

  • En muchos casos, se produce un burbujeo y debe vincularse al elemento principal.stopPropagation
e.stopPropagation
 <div className={classes.confirm_operation} onClick={e=>e.stopPropagation()}>
                    <button className={classes.confirm_operation_cancel} onClick={cancel}>取消</button>
                    <button className={classes.confirm_operation_clear} onClick={confirm}>清空</button>
                </div>

Sin cambios en la cantidad después de vaciar el carrito

  • Debido a que la copia superficial anterior usó la mayoría de los datos originales, para resolver el problema de que la cantidad aún existe después de vaciar el carrito de compras, los dos métodos siguientes

    • Use copia profunda (pero de esta manera la cantidad de datos originales no cambiará en este proyecto)
    • Al vaciar el carrito de compras, borre manualmente la cantidad dentro
     //手动清空amount数量
     temp.list.forEach(item => item.amount = 0);
     temp.total  = 0;
     temp.list = [];
     temp.money = 0;
     setCartData(temp)
    

Modo estricto useEffect y React y flujo de ejecución setState

  • Parte de la lógica de un componente React se puede escribir directamente en el cuerpo de la función del componente, como llamar a métodos como filtro y mapa en una matriz, como juzgar si se muestra un determinado componente, etc. Sin embargo, si alguna lógica se escribe directamente en el cuerpo de la función, afectará la representación del componente. Esta parte del código que producirá "efectos secundarios" no debe escribirse directamente en el cuerpo de la función.
  • Por ejemplo, si la lógica de modificar el estado se escribe directamente en el componente, hará que el componente se represente en un bucle continuo hasta que la memoria se desborde debido a demasiadas llamadas.

React.StrictMode

  • Al escribir componentes de React, debemos hacer todo lo posible para evitar aquellos códigos que producirán "efectos secundarios" en los componentes. Al mismo tiempo, si su React usa el modo estricto, es decir, si usa React.StrictModeetiquetas en React, entonces React será muy "inteligente" para verificar si hay códigos de efectos secundarios escritos en sus componentes. Por supuesto, esta inteligencia es citado, echemos un vistazo a cómo explica la documentación en el sitio web oficial de React:

El modo estricto no puede detectar automáticamente los efectos secundarios, pero puede ayudarlo a detectarlos haciéndolos un poco más deterministas. Esto se hace mediante una doble invocación intencional de las siguientes funciones:

  • Componente de clase constructor, rendery shouldComponentUpdatemétodos
  • getDerivedStateFromPropsMétodo estático de componente de clase
  • Cuerpos de componentes de función
  • Funciones de actualización de estado (el primer argumento de setState)
  • Funciones pasadas a useState, useMemoouseReducer

La palabra clave anterior se llama "doble invocación", es decir, llamadas repetidas. ¿Qué significa esta oración? Probablemente significa que React no puede encontrar automáticamente los efectos secundarios por usted, pero encontrará una manera de hacer que aparezcan para que pueda encontrarlos. Entonces, ¿cómo te llevó a descubrir los efectos secundarios? El modo estricto de React, en el modo de desarrollo , llamará activamente a algunas funciones repetidamente para que aparezcan los efectos secundarios. Entonces, cuando esté en el modo de desarrollo con el modo estricto React habilitado, estas funciones se llamarán dos veces:

Los métodos constructor, yrender del componente de clase El método del componente de clase El cuerpo de función del componente de función El parámetro es una función El parámetro es una función , oshouldComponentUpdate
getDerivedStateFromProps

setState
useStateuseMemouseReducer

Las llamadas repetidas harán que los efectos secundarios sean más fáciles de resaltar. Puede intentar llamar a uno en el cuerpo de la función de un componente de función y console.logverá que se ejecutará dos veces. Si tiene las herramientas de desarrollo de React instaladas en su navegador, la segunda llamada será Se muestra en gris.

  • Por ejemplo, si informamos este error Too many re-renders, llamamos al método directamente en el cuerpo de la función setStatey se activará

    • Pregunta: No significa que cuando el valor del nuevo estado sea el mismo que el valor anterior, no se activará la nueva representación del componente. ¿Por qué lo hace el siguiente código ?
    • Explicación: setCountcuando se llama, está en la etapa de renderizado y no juzgará si los valores son los mismos
    const App = () =>{
    	const [count,setCount] = useState(0);
    	
    	setCount(0)
    	
    	return (
    		<div>
    			{count}
    		</div
    	)
    }
    

Flujo de ejecución del componente de función setState (o setXXXXX)

setState() --> dispatchSetData()

//1.会先判断,组件当前处于什么阶段
如果是渲染阶段 --> 不会检查state的值是否相同
如果不是渲染阶段 --> 会检查state的值是否相同
	- 如果值不相同,则会对组件进行重写渲染
	- 如果值相同,则不会组件进行重写渲染
		如果值相同,React在一些情况下会继续执行当前组件的渲染
		但是这个渲染不会触发其子组件的渲染,这次渲染不会产生实际的效果
		这种情况通常发生在值第一次相同的时候

Ejemplo (en modo no estricto)

Aplicación.jsx

import React, {useState} from 'react';
import B from "./B";
const App = () => {
    console.log('App组件重新渲染了')
    const [count,setCount] = useState(0);
    
    // handleClick被点击触发的时候
    // 是在非渲染阶段了
    const handleClick = () => {
        setCount(1);
        /**
         * 初始化的时候
         *          输出 'App组件重新渲染了' 和 'B组件被重新渲染了'
         *
         * 第一次点击按钮 count 变为 1
         *          输出 'App组件重新渲染了' 和 'B组件被重新渲染了'
         *
         * 第二次点击按钮 count 变为 1
         *          输出 'App组件重新渲染了'
         *
         * 第三次点击按钮 count 变为 1
         *          未输出任何内容
         */
    }
    return (
        <div>
            我是App{count}
            <button onClick={handleClick}>点击我</button>
            <B/>
        </div>
    );
};

export default App;

B.jsx

import React from 'react';

const B = () => {
    console.log('B组件被重新渲染了')
    return (
        <div>
            我是B组件
        </div>
    );
};

export default B;

También puede echar un vistazo a este ejemplo: setTimeout es asíncrono y también se encuentra en la etapa de no procesamiento durante la ejecución, por lo que en este momento no causará llamadas repetidas ni generará demasiados errores de procesamiento.

import React, {useState} from 'react';
const App = () => {
    console.log('App组件重新渲染了')
    const [count,setCount] = useState(0);

    setTimeout(() => {
        setCount(1)
    },0)

    return (
        <div>
            我是App{count}
        </div>
    );
};

export default App;

efecto de uso

  • useEffectEs una función de enlace que requiere una función como parámetro. Esta función como parámetro se ejecutará de forma predeterminada después de que se procese el componente (es decir, la devolución de llamada solo se ejecuta en la etapa de no representación)

  • En el desarrollo real, puede escribir el código que producirá efectos secundarios useEffecten la función de devolución de llamada, de modo que pueda evitar que estos códigos afecten la representación del componente.

  • En términos sencillos, no puedo modificar el valor del estado durante la fase de representación. Necesito modificarlo después de la representación (es decir, el tiempo de ejecución de la devolución de llamada raíz)

  • Si **no desea que se llame a useEffect cada vez en la fase de renderizado,** podemos pasar el segundo parámetro

第二个参数为数组(即为依赖项目)

当数组当中的依赖项发送改变的时候,useEffect才会去调用
通过会将Effect当中所有的变量都设置为依赖项目
	这样子一来可以确保这些值发生变化时候,会触发Effect变化

像setState()是由钩子函数useState()生成的
	useState()会确保组件的每次渲染都会获取到相同的setState()对象,所以stateState可以不设置进Effect
    所以下面的setShowDetail,setShowCheckOut,可以写进去,也可以不写进去

当第二个参数为空数组,则意味着Effect只会在组件初始化的时候执行一次 

const a = 10;

//产生一个闭包
useEffect(() => {
	//确保a为最新值
	console.log(a);
	setShowDetail(false);
	setShowCheckOut(false);
},[a,setShowDetail,setShowCheckOut])
  • Combinado con las notas del maestro Tianyu.
React中的副作用操作

* 发ajax请求获取数据
* 设置订阅 /  启动定时器
* 手动更改真实DOM

import React from "react";
React.useEffect(() => {
	//do something
	// ...
	
	//返回的函数将在组件卸载前执行
	return () => {
		//在这里做一些收尾工作,会在下一次effect调用前执行
		//比如清除定时器,取消订阅
	}

},[stateValue]) //如果指定的是 [], 那么回调函数只会在第一次render后执行
				//否者就会里面的值发生变化,就执行一次回调(因为render被重新执行了)
  • Resumir
    • Si desea ejecutarlo cada vez que renderiza, no escriba nada en el segundo parámetro
    • Si desea recuperar solo cuando cambia la variable utilizada, el segundo parámetro se pasa en el nombre de la variable utilizada en forma de matriz (es decir, se escribe en el proyecto dependiente)
    • Si desea ejecutarlo solo una vez (ejecutado durante la inicialización), pase una matriz vacía como segundo parámetro[]

Transformar entrada a través de useEffct

  • La parte comentada es antes de la transformación.
import React, {useEffect, useState} from 'react';
import classes from "./index.module.css";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faSearch} from "@fortawesome/free-solid-svg-icons";
//import {throttle} from "lodash";

const Index = (props) => {
    /*使用节流*/
    //const handleChange = throttle((e) => {
    //    props.search(e.target.value);
    //},150)

    const [value,setValue] = useState('')
    const handleChange = (e) => {
        setValue(e.target.value.trim());//设置值
    }

    useEffect(() => {
        props.search(value);
    },[value])
    return (
        <div className={classes.filter}>
            <div className={classes.filter_search}>
                <FontAwesomeIcon className={classes.filter_search_icon} icon={faSearch} />
                {/*<input type="text" className={classes.filter_search_input} placeholder={'请输入关键字'} onChange={handleChange}/>*/}
                <input type="text" value={value} onChange={handleChange} className={classes.filter_search_input} placeholder={'请输入关键字'} />
            </div>
        </div>
    );
};

export default Index;

duda

  • En el pasado, configuraba la cantidad de carritos de compras + 1, y siempre habría una adición más
/*增加*/
setCartData(prevState => {
    const temp = {...prevState};
    console.log(temp.list.includes(item))
    if(!temp.list.includes(item)){
        console.log('我是不包含')
        //不包含
        item.amount = 1;
        temp.list.push(item);
    }else{
        console.log('我是包含')
        //包含
        console.log('之前',item.amount)
        item.amount +=1;
        console.log('之后',item.amount)
    }
    temp.total++;//数量+1
    return temp;
})

Después de hacer clic en el signo +, vuelvo a hacer clic en el signo + y la llamada siempre se repetirá

Debido a que el modo estricto está activado, verificará si hay efectos secundarios y se llamará dos veces (porque a veces se resaltan los efectos secundarios de llamar dos veces)

usarreductor

  • Para operar los datos generados por useState, el estado se establece en múltiples funciones (la definición de estado y la adición de métodos no están en el mismo lugar), como se muestra en la siguiente figura
//伪代码-1示例

const [carData,setCatData] = useState({
    
    })

//添加商品
const addItme = () => {
    
    };

//减少商品
const removeItme = () => {
    
    };

//清空购物车
const clearCart = () => {
    
    };


//伪代码-2示例
import React, {
    
    useState} from 'react';

const App = () => {
    
    
    const [number,setNumber] = useState(0);
    const handleReduce = () => {
    
    
        setNumber(prevState => prevState - 1);
    }
    const handleAdd = () => {
    
    
        setNumber(prevState => prevState + 1);
    }
    return (
        <div>
            <button onClick={
    
    handleReduce}>-</button>
            <span>{
    
    number}</span>
            <button onClick={
    
    handleAdd}>+</button>
        </div>
    );
};

export default App;

  • Entonces podemos usaruseReducer
useReducer(reducer,initialArg,init)
参数:
reducer:为一个函数(state,action) => stateValue
	
	reducer在执行的时候,会收到二个参数
    * state 当前最新的state
    * action(一般叫action) 普通的js对象,可以将要干的事情告诉action,
    * 进而根据action中不同值来执行不同操作
    * 为了避免type无效(区分操作的type),可以无论如何都会返回一个值
    * 说白了reducer就是将对state的不同操作定义在同一个函数当中
	
	
initialArg: state的初始值,作用和useState()中的值是一样的


返回值:为数组

第一个参数: state 用来获取state的值
第二个参数:state修改的派发器
  * 通过派发器可以发送操作state的命令
  * 具体的修改行为将会由另外一个函数(reducer)执行
  
  
  • En términos sencillos, el despacho es un despachador, y el reductor es responsable del trabajo. Los reductores se dedican a diferentes tareas de acuerdo con diferentes instrucciones.
//可以通过...arg 后输出arg查看所有参数

const [count,countDispatch] =  = useReducer((state,action) => {
	const { type } = action.type;   
	if(type === 'ADD') {
        return state + 1;
    }else if(type ==='SUB'){
        return state - 1;
    }
    return state;//避免没有type匹配失败导致问题
},1)

//或者
const [count,countDispatch] =  = useReducer((state,action) => {
	const { type } = action.type;   
	switch(type){
        case 'ADD': return state + 1;
        case 'SUB': return state -1;
        default: return state;
    }
},1)

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


const reducer = (prevState,action) => {
    const {type} = action;//对传递过来的对象进行结构
    switch (type){
        case 'SUB': return prevState - 1;
        case 'ADD': return prevState + 1;
        default: return prevState;//避免未知的
    }
}
const App = () => {
/*    const [number,setNumber] = useState(0);
    const handleReduce = () => {
        setNumber(prevState => prevState - 1);
    }
    const handleAdd = () => {
        setNumber(prevState => prevState + 1);
    }*/
    const [number,numberDispatch] = useReducer(reducer,0)
    return (
        <div>
            {/*点击按钮,调用指挥者(dispatch),指挥者再去通知reducer执行对应操作*/}
            <button onClick={() => numberDispatch({type:'SUB'})}>-</button>
            <span>{number}</span>
            <button onClick={() => numberDispatch({type:'ADD'})}>+</button>
        </div>
    );
};

export default App;


  • Reduce generalmente se define fuera del componente para evitar la creación repetida (tenga en cuenta que Reducer se crea fuera, no useReducer; de lo contrario, useReudcer creado fuera generará React Hook "useReducer" no se puede llamar en el nivel superior. React Hooks se debe llamar en una función React componente o un React personalizado)
//之前
const App = () => {
	const [count,countDispatch] = useReducer(() => {
        
    },0)
}

//修改之后
const reducer = () => { ... }
const App = () => {
	const [count,countDispatch] = useReducer(reducer,0)
}

Reaccionar.memo()

  • Hay dos casos en los que el componente se vuelve a renderizar
    • El primer tipo: el estado del componente cambia y hace que se vuelva a renderizar
    • El segundo tipo: volver a renderizar el componente principal provoca que se vuelva a renderizar el componente secundario

Para la siguiente situación, podemos usar React.memo.Podemos ver que cuando no está en uso React.memo, el valor de la aplicación cambia y los subcomponentes también cambian.

  • A veces, este tipo de renderizado es muy malo. Por ejemplo, nada en mi componente secundario ha cambiado. Es porque el componente principal ha cambiado, por lo que tengo que cambiar. Entonces, para evitar problemas de rendimiento causados ​​​​por el renderizado repetido, podemos usarReact.memo

  • React.memo() es una función de orden superior

    • Una función de orden superior es una función cuyos parámetros son funciones o cuyo valor de retorno es una función
  • El nuevo componente envuelto por React.memo() tiene función de almacenamiento en caché

    • Después de envolver, propsla nueva representación del componente solo se activará cuando el componente cambie; de ​​lo contrario, siempre se devolverá el resultado en el caché.
  • Ejemplo (en modo no estricto)

App.jsx

import React, {useState} from 'react';
import A from "./component/A";
const App = () => {
    console.log('App被渲染了')
    const [number,setNumer] = useState(0);
    const handleClick = () => {
        setNumer(prevState => prevState + 1);
    }
    return (
        <div>
            我是App
            <button onClick={handleClick}>点击我+1</button>
            {number}
            <A/>
        </div>
    );
};

export default App;


A.jsx
import React from 'react';
import B from "./B";
const A = () => {
    console.log('A被渲染了')
    return (
        <div>
            我是A组件
            <B/>
        </div>
    );
};

export default React.memo(A);

B.jsx
import React from 'react';

const B = () => {
    console.log('B被渲染了')
    return (
        <div>
            我是B组件
        </div>
    );
};

export default React.memo(B);

Efecto: cuántas veces hace clic en +1, el componente secundario no se volverá a procesar

Añadir React.memo

useCallback

  • Después de usar React.memo, la función de almacenamiento en caché se puede lograr, pero si el componente secundario recibe la función pasada del componente principal a través de accesorios, ¿qué sucederá después de usar React.memo? (Revise React.memo, los cambios de accesorios volver a renderizarse, de lo contrario no se volverá a renderizar)
App.jsx

import React, {useState} from 'react';
import A from "./component/A";
const App = () => {
    console.log('App被渲染了')
    const [number,setNumer] = useState(0);
    const handleClick = () => {
        setNumer(prevState => prevState + 1);
    }
    return (
        <div>
            我是App
            <button onClick={handleClick}>点击我+1</button>
            {number}
            <A add={handleClick}/>
        </div>
    );
};

export default App;


A.jsx
import React from 'react';
const A = (props) => {
    console.log('A被渲染了')
    return (
        <div>
            我是A组件
            <button onClick={props.add}>点击我操作App,使其数字+1</button>
        </div>
    );
};

export default React.memo(A);

En el diagrama de efectos, puede ver que incluso si se usa React.memo, cuando el componente A recibe el valor pasado por el componente principal, aún hará que el componente se vuelva a procesar debido al cambio del componente principal (porque los accesorios han cambiado)

  • El ejemplo de código anterior se debe a que la nueva representación del componente A hace que handleClickse vuelva a crear, lo que genera cambios, por lo que podemos usarlo useCalbackpara crear una función de devolución de llamada en reaccionar para evitar esta situación.
  • useCallback¿Se utiliza una función de gancho para crear una función de devolución de llamada en reaccionar?
  • useCallbackLas devoluciones de llamadas creadas no se vuelven a crear cuando el componente se vuelve a renderizar
  • useCallbackparámetro
参数:
参数1:回调函数
参数2:依赖数组
	当依赖数组的变量发生变化时,回调函数才会重新执行
	如果不指定依赖数组(也就是第二个参数什么都不写),
	回调函数每次被渲染的时候就会被重新执行
	一定要将回调函数中使用到的所有变量设置到依赖当中
		除了setState,因为这个不会变~
  • Resumir

    • Si desea ejecutarlo cada vez que renderiza, no escriba nada en el segundo parámetro
    • Si desea recuperar solo cuando cambia la variable utilizada, el segundo parámetro se pasa en el nombre de la variable utilizada en forma de matriz (es decir, se escribe en el proyecto dependiente)
    • Si desea ejecutarlo solo una vez (ejecutado durante la inicialización), pase una matriz vacía como segundo parámetro[]
  • ejemplo

import React, {useCallback, useState} from 'react';
import A from "./component/A";
const App = () => {
    console.log('App被渲染了')
    const [number,setNumer] = useState(0);
    const handleClick = useCallback(() => {
         setNumer(prevState => prevState + 1);
    },[])
    //const handleClick = () => {
    //    setNumer(prevState => prevState + 1);
    //}
    return (
        <div>
            我是App
            <button onClick={handleClick}>点击我+1</button>
            {number}
            <A add={handleClick}/>
        </div>
    );
};

export default App;

Se puede ver que incluso si la aplicación se reescribe y renderiza, el componente A no se volverá a renderizar, porque la devolución de llamada en la aplicación está configurada para ejecutarse solo una vez cuando se crea.

representaciones

(todo) Uso de Strapi

usar buscar

  • Aquí hay un registro simple, puede ver @mdn-fetch para más detalles
  • hay que ser consciente de es
    • En otras palabras, excepto cuando la red falla y la solicitud se bloquea , todas las demás situaciones se resuelven.
    • Por lo tanto, podemos okrealizar las operaciones correspondientes juzgando
当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject,即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve(如果响应的 HTTP 状态码不在 200 - 299 的范围内,则设置 resolve 返回值的 ok 属性为 false),仅当网络故障时或请求被阻止时,才会标记为 reject。
  • Damos salida para ver los resultados devueltos por la solicitud.
fetch('http://localhost:3000/list')
.then(res => {
    
    
	console.log(res)
})
.catch(error=>{
    
    
	
})

  • Entonces se puede hacer así, manejando datos y errores.
fetch('http://localhost:3000/list1414')
    .then(res => {
    
    
        const {
    
    ok} = res;//获取请求结果
        if(ok){
    
    
            //请求成功
            return res.json();
        }
        throw new Error('网络异常')
    })
    .then(res => {
    
    
        console.log(res.data
                   );//根据接口不同而返回的数据不同
    })
    .catch(error=>{
    
    
    	//error.message获取错误文本信息
        console.log(error.message);//输出错误信息
    })
  • El estudiante de ejemplo no realiza ninguna modificación lógica, por lo que solo se muestra el contenido de app.jsx
import React, {useState,useEffect} from 'react';
import StudentList from "./components/StudentList";
import './App.css';
const App = () => {
    const [stuData, setStuData] = useState([]);
    const [isLoading,setLoading] = useState(false);//是否正在加载
    const [isError,setError] = useState(false);//是否有错误
    useEffect(() => {
        setLoading(true);
        setError(false)
        fetch('http://localhost:3000/list')
            .then(res => {
                const {ok} = res;//获取请求结果
                setLoading(false);
                if(ok){
                    //请求成功
                    return res.json();
                }
                throw new Error('网络异常')
            })
            .then(res => {
                setStuData(res);
            })
            .catch(error=>{
                setError(true)
                console.log(error.message);//输出错误信息
            })
    },[])
    return (
        <div className="app">
            {/*不处于加载的时候就展示列表*/}
            { !isLoading && !isError && <StudentList stus={stuData}/> }
            {/*加载状态*/}
            { isLoading && '加载数据中...' }
            {/*是否出错*/}
            {isError && '数据出错...'}
        </div>
    );
};

export default App;

  • Por supuesto, también puedes usarasync 和 await
    • Nota; el primer parámetro de useEffect no puede ser asíncrono
    • Ahora sé que try catchhay más finally...
import React, {useState,useEffect} from 'react';
import StudentList from "./components/StudentList";
import './App.css';
const App = () => {
    const [stuData, setStuData] = useState([]);
    const [isLoading,setLoading] = useState(false);//是否正在加载
    const [isError,setError] = useState(false);//是否有错误
    useEffect(() => {
        const fetchData = async () => {
            try {
                setLoading(true);
                setError(false)
                const res = await fetch('http://localhost:3000/list');
                if(res.ok){
                    //请求成功
                    const data = await res.json();

                    setStuData(data)
                }else{
                    throw new Error('网络异常')
                }
            }catch (e){
                setError(e)
            }finally {
                setLoading(false);//结束加载loading
            }
        }
        fetchData();
    },[])
    return (
        <div className="app">
            {/*不处于加载的时候就展示列表*/}
            { !isLoading && !isError && <StudentList stus={stuData}/> }
            {/*/!*加载状态*!/*/}
            { isLoading && '加载数据中...' }
            {/*/!*是否出错*!/*/}
            {isError && '数据出错...'}
        </div>
    );
};

export default App;

Ganchos personalizados (selfHooks)

  • De hecho, un enlace personalizado es una función ordinaria, pero su nombre debe comenzar con uso (un enlace personalizado es una encapsulación de otros enlaces)

redux,RTK,RTKQ

reduex核心思想
	多个state放置在一起去统一管理
	
Reducer
	对于state的所有操作,都保存到同一个函数
	
Store
	仓库对象,无论是订阅还是派发任务,都是通过store
	根据reducer创建对应的store
	
dispatch(Store里面的方法)
	dispatch依旧是告诉reducer怎么去处理state数据
	怎么理解从Store里面调用dispatch呢?因为store根据reducer创建的,所以想要操作数据,自然也应该是从store触发
	
getState(),从store获取数据

因为redux是适合所有的js的,所以是发布者,订阅者模式
所以store.subscribe(回调函数),发生变化的时候就会执行回调函数
function reducer(prestate,action){
    
    
	preState 即将更新前state的值,reducer的返回值将作为state的新值
	action 是一个普通的js对象,可以保存操作的信息
		type表示操作类型
		其他需要传递的参数,也可以在action设置
}

通常存储的是一个对象
const store = Redux.createStore(reducer,初始值);
	//也可以在reducer指定初始值
	function reducer(preState = 1 ,action) {
    
    .....}

El uso de redux en html.

  • Opere en números haciendo clic en agregar eliminar

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Redux</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/redux/4.2.0/redux.min.js"></script>
</head>
<body>
<button onclick="handleClickReduce()">减少</button>
<span id="number">1</span>
<button onclick="handeClickAdd()">增加</button>
<script>
    const spanNumber = document.getElementById('number')
    // 1.创建reducer
    const reducer = (preState,action) => {
      
      
        const {
      
      type} = action;
        switch(type){
      
      
            case 'ADD': return preState + 1 ;
            case 'SUB': return preState - 1;
            default: return preState;
        }
    }
    // 2.根据reducer创建store
    const store = Redux.createStore(reducer,1);
    // 4.发生改变的回调
    store.subscribe(() => {
      
      
        console.log('发生改变了',store.getState())
        spanNumber.textContent = store.getState();
    })
    const handleClickReduce = () => {
      
      
        // 3.store进行改变
        store.dispatch({
      
      type:'SUB'})
    }
    const handeClickAdd = () => {
      
      
        // 3.store进行改变
        store.dispatch({
      
      type:'ADD'})
    }
</script>
</body>
</html>

  • (todo) Si necesita especificar y agregar cualquier número, puede pasarlo a través de despacho (la segunda acción del reductor es en realidad un objeto ordinario)

Algunas desventajas de redux

  • Si el estado es demasiado complejo, será muy difícil de mantener
    • Se puede resolver agrupando el estado, creando múltiples reductores y luego fusionándolos en uno
  • Cada vez que se opera el estado, el estado debe copiarse y luego modificarse.Si necesita modificar el valor de la tercera capa, ¿no es muy complicado?
  • Las constantes detrás del caso son más problemáticas de mantener
  • Los dos últimos redux también están pensados, por lo que se pueden usar Redux Toolkit(RTK), RTK puede reemplazar completamente a redux, RTK incluye redux

RTK

  • Instale el conjunto completo (no olvide instalar react-redux)
# NPM
npm install @reduxjs/toolkit react-redux

# Yarn
yarn add @reduxjs/toolkit react-redux
  • Crear el fragmento de repositorio correspondiente
import {
    
    createSlice} from "@reduxjs/toolkit";

//1. 创建reducer的切片
//需要一个配置对象作为参数,通过对象的不同属性来指定它的配置
//会自动生成action
const stuSlice = createSlice({
    
    
    name:'stu',//参与自动生成的action
    //state的初始值
    initialState: {
    
    
        name:'动感超人',
    },
    //操作对应的state方法,通过不同方法操作state值
    reducers:{
    
    
        /*这个state是一个state代理,操作这个会影响到store对应的数据*/
        setName(state,action){
    
    
            state.name = '西瓜超人';//直接影响到state数据
        }
    }
});

//返回 {type: 'stu/setName', payload: '进去的参数'}
console.log(stuSlice.actions.setName('进去的参数'));

Ver resultados de salidastuSlice

  • Crear reductor y exponer
import {
    
    createSlice,configureStore} from "@reduxjs/toolkit";

//1. 创建reducer的切片
//需要一个配置对象作为参数,通过对象的不同属性来指定它的配置
//会自动生成action
const stuSlice = createSlice({
    
    
    name:'stu',//参与自动生成的action
    //state的初始值
    initialState: {
    
    
        name:'动感超人',
        age:18,
    },
    //操作对应的state方法,通过不同方法操作state值
    reducers:{
    
    
        /*这个state是一个state代理,操作这个会影响到store对应的数据*/
        setName(state,action){
    
    
            state.name = '西瓜超人';//直接影响到state数据
        },
        setAge(state,action){
    
    

        }
    }
});

//返回 {type: 'stu/setName', payload: '进去的参数'}
//console.log(stuSlice.actions.setName('进去的参数'));

//暴露所有生成action的函数
export const {
    
    setName,setAge} = stuSlice.actions;

//创建仓库并暴露
export default configureStore({
    
    
    //reducer:stuSlice,
    //或者传入一个对象
        //key值用于标识不同的reducer
        //value则传入对应的reducer,
    reducer:{
    
    
        student:stuSlice.reducer,
    }
})

  • usar redux
    • 使用Providertienda de pases
    • recipiente
      • Al useSelectoreobtener el estado correspondiente
      • Obteniendo useDispatchel objeto de la operación de distribución tienda (es decir, despacho)
      • Genere el objeto del tipo de operación y el valor de la operación a través de la función expuesta (es decir, el valor de retorno de setName)

src/index.js

import ReactDOM from "react-dom/client"
import App from "./App"
import {Provider} from "react-redux";
import store from "./store/index";
const root = ReactDOM.createRoot(document.getElementById('root'))

root.render(
    <Provider store={store}>
        <App/>
    </Provider>
)

Aplicación.jsx

import React from 'react';
import {useSelector,useDispatch} from "react-redux";
import {setName} from "./store";

const App = () => {
    const store = useSelector(state => state.student);//获取student的store
    const dispatch = useDispatch();//获取派发器对象
    const handleClick = () => {
        //传递要设置的新名字
        dispatch(setName('我是动感超人'+Date.now()))
    }
    return (
        <div>
            {store.name}
            <button onClick={handleClick}>更改我的名字</button>
        </div>
    );
};

export default App;

Algunas deficiencias y soluciones.

  • Si se crean varias porciones y cada reductor de porciones tiene un método setName, el método expuesto tendrá el mismo nombre
  • Solución: diferentes segmentos de diferentes archivos (de hecho, los reductores están separados)

tienda/index.js

import {configureStore} from "@reduxjs/toolkit";
import stuReducer from "./stuSlice"
import addressReducer from "./addressSlice"

export default configureStore({
    reducer:{
        student: stuReducer,
        address: addressReducer,
    }
})

tienda/stuSlice.js

import {createSlice} from "@reduxjs/toolkit";

const stuSlice = createSlice({
    name:'stu',
    initialState:{
        name:'傻瓜超人',
        age:18,
    },
    reducers:{
        setName(state,action){
            state.name = action.payload;
        },
        setAge(state,action){
            state.age = action.payload;
        }
    }
});
export const {setName,setAge} = stuSlice.actions;

export default stuSlice.reducer;

store/addressSlice.js

import {createSlice} from "@reduxjs/toolkit"

const addressSlice = createSlice({
    name:'address',
    initialState:{
      address:'高老庄'
    },
    reducers:{
        setAddress(state,action){
            state.address = action.payload
        }
    }
})
export const {setAddress} = addressSlice.actions;
export default addressSlice.reducer;

usar

import React from 'react';
import {useSelector,useDispatch} from "react-redux";
import {setName,setAge} from "./store/stuSlice";
import {setAddress} from "./store/addressSlice";

const App = () => {
    const { student,address }  = useSelector(state => state);
    const dispatch = useDispatch();
    return (
        <div>
            <span>姓名:{student.name}</span>
            <span>年龄:{student.age}</span>
            <span>地址:{address.address}</span>
            <button onClick={() => dispatch(setName('张三'))}>设置姓名为"张三"</button>
            <button onClick={() => dispatch(setAge(888))}>设置年龄为888</button>
            <button onClick={() => dispatch(setAddress('地球村'))}>设置地址为"地球村"</button>
        </div>
    );
};

export default App;

RTKQ (consulta del kit de herramientas redux)

  • RTK no solo nos ayuda a resolver el problema del estado, sino que también nos proporciona RTK Query para ayudarnos a lidiar con el problema de la carga de datos. RTK Query es una poderosa herramienta de adquisición y almacenamiento en caché de datos. Con su ayuda, la carga en aplicaciones web se vuelve muy simple y elimina la necesidad de escribir la lógica para obtener y almacenar datos en caché por nosotros mismos.

  • Problemas que deben solucionarse al cargar datos en aplicaciones web:

    • Mostrar diferentes componentes de la interfaz de usuario según los diferentes estados de carga
    • Reduzca las solicitudes repetidas de los mismos datos
    • Use actualizaciones optimistas para mejorar la experiencia del usuario
    • Administre el ciclo de vida del caché a medida que el usuario interactúa con la interfaz de usuario
  • En el pasado, creamos el estado correspondiente cada vez que enviamos una solicitud, por ejemplo, isLoadingpara isErrorprocesar el estado durante la carga. Con RTKQ, no necesitamos hacer estas cosas. Usamos RTKQ para ayudarnos a completar este asunto.

  • Para obtener más explicaciones y enseñanzas, consulte el RTKQ de @李立超

Paso 1: Configurar RTKQ

tienda/studentApi.js

import {
    
    createApi, fetchBaseQuery} from "@reduxjs/toolkit/dist/query/react";

//创建Api对象
//createApi()用来创建RTKQ中的API对象
const studentApi = createApi({
    
    
    //Api的reducer标识,不能和其他Api或reducer重复
    reducerPath:'studentApi',
    //指定查询的基础信息,发送请求使用的工具
    //用来设置发送请求的工具,就是你是用什么发请求,RTKQ为我们提供了fetchBaseQuery作为查询工具,它对fetch进行了简单的封装,很方便,如果你不喜欢可以改用其他工具,这里暂时不做讨论。
    baseQuery:fetchBaseQuery({
    
    
        baseUrl:'http://localhost:3000/'
    }),
    endpoints:(build) => {
    
    
        //build是请求的构建器,通过build来设置请求相关的信息
        return {
    
    
            //使用构建器来创建请求对象
            getStudents:build.query({
    
    
                query(arg) {
    
    
                    //用来指定请求的子路径
                    //可以直接返回请求的子路径,默认为get(axios也是默认get方式)
                    //也可以返回配置对象
                    return 'students';
                },
                //可以在这里将请求过来的数据进行转换并将结果返回
                transformResponse(baseQueryReturnValue){
    
    
                    return 转换后的结果
                }
            })
        }
    }
})

//api对象创建后,对象中会根据各种方法自动的生成对应的钩子函数
//通过这些钩子函数,可以向服务器发送请求
//钩子函数的命名规则 endpoints中有一个getStudents 结合查询方式=>生成 useGetStudentsQuery
export const {
    
     useGetStudentsQuery } = studentApi;

//我们还需要使用store
export default studentApi;

Paso 2: crea un objeto de tienda

tienda/index.js

import {
    
    configureStore} from "@reduxjs/toolkit";
import studentApi from "./studentApi";
export default configureStore({
    
    
    reducer:{
    
    
        [studentApi.reducerPath]:studentApi.reducer,
    },

    middleware:getDefaultMiddleware => {
    
    
        //getDefaultMiddleware调用后会返回所有的默认中间件
        //传入studentApi的缓存生效
        return getDefaultMiddleware().concat(studentApi.middleware)
    }
})

Luego, el archivo de entrada principal src/index.js

import ReactDOM from "react-dom/client"
import App from "./App"
import store from "./store"
import {
    
    Provider} from "react-redux";
const root = ReactDOM.createRoot(document.getElementById('root'))

root.render(
    <Provider store={
    
    store}>
        <App/>
    </Provider>
)

Paso 3: Uso de RTKQ

  • Salida Ver el valor de retorno de la consulta de enlace en la API de llamada
    • Se puede ver que se ha emitido tres veces, lo que se puede entender simplemente como antes de enviar, enviar y enviar completado
    • Cada etapa será identificada por campos específicos, tales como isLoading,isFetching,isSuccess,isErroretc.
    • De esta forma nos resulta fácil operar (al menos no tenemos que escribir isLoading, isError, RTKQ lo ha hecho por nosotros)
import React from 'react';
import {useGetStudentsQuery} from "./store/studentApi";

const App = () => {
    //调用api查询数据
    //这个钩子函数会返回一个对象作为返回值,请求过程中相关数据都在该对象中
    const obj = useGetStudentsQuery();//调用Api中的钩子查询数据
    console.log(obj)
    return (
        <div>
            我是App
        </div>
    );
};

export default App;

  • Ejemplos de uso específicos
import React from 'react';
import {useGetStudentsQuery} from "./store/studentApi";

const App = () => {
    //调用api查询数据
    //这个钩子函数会返回一个对象作为返回值,请求过程中相关数据都在该对象中
    const { isLoading,data,isSuccess} = useGetStudentsQuery();//调用Api中的钩子查询数据
    return (
        <div>
            { isLoading && '加载中...'}
            {isSuccess && data.map(item =>
                <p key={item.id}>
                    姓名:{item.name} <br/>
                    性别:{item.sex} <br/>
                    年龄:{item.age} <br/>
                    地址:{item.address} <br/>
                </p>)
            }
        </div>
    );
};

export default App;

Efecto

estructura de la base de datos

Editar información Usar RTK para mantener los datos actualizados

  • Al editar los datos de los estudiantes anteriormente, los datos se pasaban a través de accesorios, pero si alguien modificaba los datos y no los actualizábamos, haría que usáramos datos antiguos al editar y la experiencia del usuario no fuera buena.
  • Entonces podemos volver a solicitar el servidor y volver a consultar los datos al editar
  • La clave para usar RTKQ es cómo pasar parámetros y cómo definir la interfaz
//接口定义
const studentApi = createApi({
    
    
    // ....
      endpoints:(build) => {
    
    
        //build是请求的构建器,通过build来设置请求相关的信息
        return {
    
    
            getStudents:build.query({
    
    
                query() {
    
    
                    //用来指定请求的子路径
                    return 'students';
                }
            }),
            getStudentInfo:build.query({
    
    
                query(id) {
    
    
                    return `students/${
      
      id}`;
                }
            })
        }
    }
    // ....
})
  
export const {
    
     useGetStudentsQuery,useGetStudentInfoQuery} = studentApi;


//传参-使用的时候传参
//传入id,用作查询的参数
const {
    
    data,isSuccess} = useGetStudentInfoQuery(props.id);//异步请求
const [formData,setFormData] = useState({
    
    
        name: props.name ? props.name : "",
        sex: props.sex ? props.sex : "男",
        age: props.age ? props.age : "",
        address: props.address ? props.address : "",
    });
//使用useEffect重新渲染数据当状态改变
//为了避免在渲染中操作state导致无限重复渲染,我们选择在useEffect
useEffect(() => {
    
    
    if(isSuccess){
    
    
        setFormData({
    
    
            ...data,
        })
    }
},[isSuccess])

Establecer el tiempo de caché y la consulta de conversión de datos

  • RTKQ tiene su propio caché, el valor predeterminado es de 60 segundos,
  • Cuando hacemos clic para modificar, se enviará una solicitud a segundo plano. En este momento, los datos ya están almacenados en caché. Si hacemos clic para modificar nuevamente, los datos se han almacenado en caché, por lo que la solicitud no se enviará. Sin embargo, si alguien modifica los datos, está limitado por el caché. No volverá a solicitar, por lo que podemos configurar el tiempo de caché keepUnusedDataFor , el valor predeterminado es 60 segundos
  • Cada consulta de interfaz puede establecer el tiempo de caché, el valor predeterminado es 60 (en segundos)
const studentApi = createApi({
    
    
    // ....
      endpoints:(build) => {
    
    
        //build是请求的构建器,通过build来设置请求相关的信息
        return {
    
    
            getStudents:build.query({
    
    
                query() {
    
    
                    //用来指定请求的子路径
                    return 'students';
                }
            }),
            getStudentInfo:build.query({
    
    
                query(id) {
    
    
                    return `students/${
      
      id}`;
                },
                //设置缓存时间为5秒
                keepUnusedDataFor:5,
                //加工处理请求过来的数据
                transformResponse(baseQueryReturnValue){
    
    
                    return 转换后的结果
                }
            })
        }
    }
    // ....
})

Ver el valor de retorno de useQuery

Mire la imagen primero, emita el valor de retorno de useQuery

  • refetchfunción

    • Una función para recargar datos.
  • status:string - el estado de la solicitud

    • pedding: los datos se están cargando
    • cumplido: la carga de datos está completa
  • isFetching:boolean: si los datos se están cargando , no importa cuántas veces se carguen, su estado se establecerá

  • isLoading:boolean - Indica si los datos se cargan por primera vez , refetcheste estado no se cambiará llamando

  • isSuccess:boolean - si la solicitud fue exitosa

  • isUninitialized:booleano: indica si la solicitud no ha comenzado a enviarse, a menudo se usa en solicitudes de operaciones de eliminación

  • error: el objeto existe solo si hay un error en el objeto, y no existe tal cosa si no hay error

  • data: Los últimos datos devueltos (cuando se reenvía la solicitud, se guardarán los datos devueltos por la solicitud anterior)

  • currentData: Los últimos datos del parámetro actual, (cuando el parámetro cambia, se borrará (se volverá indefinido))

    • Por ejemplo, cuando busco el nombre de la hamburguesa deseada en una lista, si quiero borrar los datos originales y mostrar el ícono de espera durante la búsqueda, y mostrar los datos devueltos después de cargar los datos, puedo usarlo. No quiero borrar los datos originales, puedo esperar currentDatahasta que Después de que los datos regresen para reemplazar los datos originales, puede usardata

parámetros de configuración useQuery

  • El primer parámetro de useQuery puede ser el parámetro pasado por la función de consulta de cada solicitud
//比如传入id
import {
    
    useGetStudentInfoQuery} from "../store/studentApi";
const {
    
    data,isSuccess} = useGetStudentInfoQuery(id);//异步请求

//则我们可以在api当中接收
const studentApi = createApi({
    
    
	//...
	endpoints:(build) => {
    
    
		getStudentInfo:build.query({
    
    
			query(id){
    
    
				return `students/${
      
      id}`
			}
		j
		})
	}
	//...
})
  • El segundo parámetro de useQuery puede pasar un objeto a través del cual se puede configurar la solicitud

    • Se puede decir que la configuración de la API pasada durante la creación es la configuración predeterminada
    • La configuración del segundo parámetro que se pasa durante el uso se puede personalizar más, y cuando haya un conflicto con la configuración predeterminada, esta prevalecerá.
  • selectFromResultFunción, el valor de retorno se utiliza como valor de retorno de useQuery

    • Por ejemplo, puedo configurar los datos de datos y filtrar los datos
    const res = useGetStudentsQuery(null,{
          
          
            selectFromResult: (result) => {
          
          
                if(result.data){
          
          
                    result.data  =  result.data.filter(item => item.age < 18)
                }
                return result;
            }
        });
    
  • pollingIntervalEl valor predeterminado es 0, establezca el intervalo de sondeo (envíe solicitudes a intervalos), en milisegundos, 0 significa que no hay sondeo

  • skipEstablezca si desea omitir la solicitud actual, el valor predeterminado es falso

    • Por ejemplo, si un componente tiene tanto la función de edición como la función de adición, debemos omitir la solicitud al agregar la función; de lo contrario, solicitaremos los datos de inicialización del fondo al agregar la función.
    //当有id的时候,才请求数据,否则不请求
    const {
          
          data,isSuccess} = useGetStudentInfoQuery(props.id,{
          
          
            skip:!props.id,
    });//异步请求
    
  • refetchOnMountOrArgChange: La configuración falsa predeterminada para recargar los datos cada vez (es decir, si configurar si usar el caché) también se puede configurar en un número, si es un número, es para configurar el período de validez del caché

  • refetchOnFocus: predeterminado falso, ya sea para recargar datos al recuperar el enfoque (como cambiar de página)

    • Si se establece en verdadero, debe configurarse en la tiendasetupListeners(store.dispatch)
    import {
          
          setupListeners} from "@reduxjs/toolkit/query"
    
  • refetchOnReConnect: Predeterminado falso, ya sea para recargar datos después de la reconexión (sin red, reconectarse a la red)

El constructor RTKQ construye una solicitud de API

  • Si lo que se envía no es obtener información (por supuesto, obtener también puede establecer objetos de configuración), es imposible que escribamos una consulta como antes.
// 之前的写法
const studentAPi = createApi({
    
    
	//...
	
	endpoints:(build) => {
    
    
		return {
    
    
			getStudent:build.query({
    
    
				query(){
    
    
					return 'students'
				}
			})
		}
	}
	
	
	//...
})

//如果现在是put或者delete或者post,就需要返回配置对象的形式了
//并且更改构建器为mutation
const studentApi = createApi({
    
    
	//...
	
	endpoints:(build) => {
    
    
		return {
    
    
			delStudent:build.mutation({
    
    
				query(参数){
    
    
					return {
    
    
						url:'students/${参数}',
						method:'delete',
						//如果有参数,则需要添加body
						body:传递的数据
					}
				}
			})
		}
	
	}
	
	//...
})
  • La llamada de eliminación no es lo mismo que la llamada de consulta, porque la eliminación no se elimina inmediatamente después de cargar la página, pero la operación de eliminación se realiza después de que el usuario confirma la eliminación, lo mismo es cierto para la operación de adición y la modificación . es también
//引入删除的接口
import {
    
    useDelStudentInfoMutation} from "../store/studentApi";
//输出查看内容
const a = useDelStudentInfoMutation();

//打印的内容
[f,{
    
    isError,isLoading,isSuccess,isUninitialized,originalArgs,reset,status}];

//第一个为触发器,第二个是结果集

//所以具体中我们可以获取触发器,在需要的时候调用就会执行函数
//调用的结果和状态和可以从结果集获取
const [delStudent,{
    
    isSuccess}] = useDelStudentInfoMutation();


//点击删除学生,正常的操作应该要询问用户是否删除的
const handleClick = useCallback(async (id) => {
    
    
    delStudent(id);
})

Agregar y modificar la operación específica de jsx

const [formData,setFormData] = useState({
	name: props.name ? props.name : "",
	sex: props.sex ? props.sex : "男",
	age: props.age ? props.age : "",
	address: props.address ? props.address : "",
});
//修改数据
const [editStudentInfo,{isSuccess:editSuccess}]  = useEditStudentInfoMutation();
//添加数据
const [addStudent]  = useAddStudentInfoMutation();

/*添加回调确认*/
const handleAdd = useCallback(async () => {
	//执行添加数据
	addStudent({
		...formData,
	});
	//清空数据
	setFormData({
		name: "",
		sex: "男",
		age:  "",
		address:  "",
	})
});
/*修改回调确认*/
const handleEditConfirm = useCallback(async () => {
	//执行修改数据
	editStudentInfo({
		id:props.id,
		info:formData,
	});
},)

La definición de consulta de studentApi.js

//修改数据
editStudentInfo:build.mutation({
    
    
	query(newInfo) {
    
    
		return {
    
    
			url:`students/${
      
      newInfo.id}`,
			method:'put',
			body:newInfo.info,
		}
	}
}),
//添加数据
addStudentInfo:build.mutation({
    
    
	query(info){
    
    
		return {
    
    
			url:'students',
			method:'post',
			body:info,
		}
	}
})

Etiquetas de datos RTKQ

  • Etiquete los datos solicitados por el segmento API. Cuando la etiqueta deja de ser válida, se volverá a cargar la solicitud correspondiente a la etiqueta.

  • Puede echar un vistazo al breve @知行合一_down-to-earth-RTKQ escrito por esta persona , etiquetando los datos solicitados por la porción Api

  1. Primero necesitamos crear la etiqueta.
const studentApi = createApi({
    
    
	reducerPath:'xxxxx',
	//用来指定API中的标签类型
	tagTypes:['student'],
	
	endpoints:(buidl) => {
    
    
		//....
	}
})
  1. Agregar etiquetas a los datos solicitados

    • Agregue atributos a la función de gancho del segmento API providesTags. El valor del atributo puede ser una cadena de matriz, un objeto de matriz o una función de devolución de llamada.
    const studentApi = createApi({
          
          
    	tagTypes:['student'],
    	endpoints:(build) => {
          
          
    		return {
          
          
    			xxxxx:build.query({
          
          
    				query(){
          
          
    					return xxxx
    				},
    				providesTags:xxxxx
    			})
    		}
    	}
    })
    
    • cuando providesTagses una cadena de matriz
      • Equivalente abreviado de objeto de matriz
      • Siempre y cuando sea del tipo y invalidatescorrespondiente, se volverán a solicitar los datos
    providesTags:['student'];
    //等同于
    providesTags:[{
          
          type:'student'}]
    
    • cuando providesTagsun objeto de matriz
      • Todos los datos (tipo e id) deben invalidatescorresponder a los datos de nueva solicitud
      • Si la identificación es un número de cadena, si hay una identificación correspondiente, seguirá fallando
    providesTags:[{
          
          type:'student',id:100}];
    providesTags:[{
          
          type:'student',id:'100'}];
    
    • Cuando providesTagses una función de devolución de llamada
    当providesTags属性的值为回调函数时,可以对标签的生效范围做更细致的划分
    参数1:网络请求的返回结果
      参数2:错误信息
      参数3:钩子函数中传入的实参
      //返回值为一个数组,符合数组元素条件的数据将生效
    providesTags:(result,error,params,meta) => {
          
          
    	return [{
          
          type:'student',id:params.id}]
    }
    
  2. Establece la etiqueta a invalidar sobre los datos solicitados

    • invalidatesTagsestablecido por
    const studentApi = createApi({
          
          
    	tagTypes:['student'],
    	endpoints:(build) => {
          
          
    		return {
          
          
    			delStudent:build.mutation({
          
          
    				query(id){
          
          
    					return {
          
          
    						url:xxxx,
    						method:'delete',
    					}
    					invalidatesTags:xxxx
    				}
    			})
    		}
    	}
    })
    
    • cuando invalidatesTagses una cadena de matriz
      • Siempre que providesTagsse incluya el tipo invalidatesTags, se volverán a solicitar los datos
      • El método de escritura de cadena de matriz es equivalente a la abreviatura de objeto de matriz
    invalidatesTags:['student'];
    
    providesTags:['student'];//让其失效
    providesTags:[{
          
          type:'student'}];//让其失效
    providesTags:[{
          
          type:'student',id:100}];//让其失效
    
    invalidatesTags:['student'];
    等同于,二个效果是一样的
    invalidatesTags:[{
          
          type:'student'}];
    
    • cuando invalidatesTagsun objeto de matriz
      • Especificar la identificación invalida la etiqueta correspondiente tanto al tipo como a la identificación
      • Sin especificar es lo mismo que las reglas de invalidación de cadenas de matriz
    invalidates:[{
          
          type:'student',id:10}];
    
    providesTags:['student'];//不会失效
    providesTags:[{
          
          type:'student'}];//不会失效
    providesTags:[{
          
          type:'student',id:10}];//会失效
    
    //实测会
    providesTags:[{
          
          type:'student',id:'10'}];//会失效
    
    providesTags:[{
          
          type:'student',id:888}];//不会失效
    
    • Cuando invalidatesTagses una función de devolución de llamada
    invalidatesTags:(result,error,stu,meta) => 
    {
          
          
    	return [
            {
          
          type:'student',id:stu.id},
            {
          
          type:'student',id:'LIST'}
        ]
    }
    
  • ejemplo, ejemplo del maestro
    • Cuando se agregan datos, la lista se actualizará
    • Después de editar, la lista se actualizará, pero si los datos no han cambiado, al hacer clic en Editar no se volverá a consultar (bajo la premisa de caché)
import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/dist/query/react";

//创建Api对象
//createApi()用来创建RTKQ中的API对象
const studentApi = createApi({
    reducerPath:'studentApi',//Api的标识,不能和其他Api或reducer重复
    //指定查询的基础信息,发送请求使用的工具
    baseQuery:fetchBaseQuery({
        baseUrl:'http://localhost:3000/'
    }),
    //用来指定api当中的标签
    tagTypes:['student'],
    endpoints:(build) => {
        //build是请求的构建器,通过build来设置请求相关的信息
        return {
            getStudents:build.query({
                query() {
                    //用来指定请求的子路径
                    return 'students';
                },
                providesTags:[{type:'student',id:'initList'}]
            }),
            //编辑的时候获取信息
            getStudentInfo:build.query({
                query(id) {
                    return `students/${id}`;
                },
                providesTags:(result, error, arg, meta) => {
                    return [
                        {type:'student',id:arg},
                    ]
                },
                设置缓存时间为5秒
                //keepUnusedDataFor:5,
            }),
            //删除数据
            delStudentInfo:build.mutation({
                query(id){
                    return {
                        url:`students/${id}`,
                        method:'delete',
                    }
                },
                invalidatesTags:[{type:'student',id:'initList'}]
            }),
            //修改数据
            editStudentInfo:build.mutation({
                query(newInfo) {
                    return {
                        url:`students/${newInfo.id}`,
                        method:'put',
                        body:newInfo.info,
                    }
                },
                //修改数据的时候,只针对修改过的列表进行重新查询数据,未改变的不变动
                invalidatesTags:(result, error, arg, meta) => {
                    return [
                        {type:'student',id:'initList'},
                        {type:'student',id:arg.id},
                    ]
                }
            }),
            //添加数据
            addStudentInfo:build.mutation({
                query(info){
                    return {
                        url:'students',
                        method:'post',
                        body:info,
                    }
                },
                //添加数据的时候重新刷新列表
                invalidatesTags:[{type:'student',id:'initList'}]
            })
        }
    }
})

//api对象创建后,对象中会根据各种方法自动的生成对应的钩子函数
//通过这些钩子函数,可以向服务器发送请求
//钩子函数的命名规则 endpoints中有一个getStudents 结合查询方式=>生成 useGetStudentsQuery
export const { useGetStudentsQuery,
    useGetStudentInfoQuery,
    useDelStudentInfoMutation,
    useAddStudentInfoMutation,
    useEditStudentInfoMutation,
} = studentApi;

//我们还需要使用store
export default studentApi;

RTKQ usando axios

  • De hecho, es muy simple de usar, es el paquete secundario de axios
  1. Instalar
npm install axios
  1. Cambiar
import {
    
    createApi} from "@reduxjs/toolkit/dist/query/react";
import axios from "axios";

const studentApi = createApi({
    
    
	 reducerPath:'studentApi',//Api的标识,不能和其他Api或reducer重复
    //指定查询的基础信息,发送请求使用的工具
    //baseQuery:fetchBaseQuery({
    
    
    //    baseUrl:'http://localhost:3000/'
    //}),
    //更改为axios
    baseQuery:axios.create({
    
    
       baseURL:'http://localhost:3000/'
    }),
})
  1. solicitar cambio de cuerpo

    • Por ejemplo, los parámetros como publicar o poner necesitan usar datos en lugar de cuerpo
    //修改数据
    editStudentInfo:build.mutation({
          
          
    	query(newInfo) {
          
          
    		return {
          
          
    			url:`students/${
            
            newInfo.id}`,
    			method:'put',
                //之前fetch的时候
                //body:newInfo.info
    			data:newInfo.info,
    		}
    	},
    	//修改数据的时候,只针对修改过的列表进行重新查询数据,未改变的不变动
    	invalidatesTags:(result, error, arg, meta) => {
          
          
    		return [
    			{
          
          type:'student',id:'initList'},
    			{
          
          type:'student',id:arg.id},
    		]
    	}
    }),
    

reaccionar-enrutador-dom@5

  • Instalar
yarn add react-router-dom@5
  • usar
    • Habilidad, puede alias el modo de enrutamiento, cuando lo cambiamos, no necesitamos cambiar el componente

entrada principal de index.js

import ReactDOM from "react-dom/client"
import App from "./App"
import {
    
    BrowserRouter as Router} from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById('root'))

root.render(
    <Router>
        <App/>
    </Router>
)

Aplicación.jsx

  • Usar Ruta para registrar la ruta

const App = () => {
    return (
        <div>
            <Link to="/">主页</span>
            <Link to="/about">关于</span>
            <Route path={'/'} component={Home}></Route>
            <Route path={'/about'} component={About}></Route>
        </div>
    );
};

export default App;
  • Aviso
    • De forma predeterminada, la ruta no coincide estrictamente. Siempre que el encabezado de la dirección URL sea consistente con la ruta, el componente se montará sin verificar la subruta (es decir, si la dirección URL contiene la ruta, el componente se montará ) .
  • Por lo tanto, habrá problemas con el código anterior. Cuando ingresamos , el componente /aboutse cargará y , debido a que se usa la coincidencia aproximada, podemos usar la coincidencia exactaHomeAbout
<Route exact path={'/'} component={Home}></Route>
<Route exact path={'/about'} component={About}></Route>

Enlace de navegación

  • Para la versión especial de Link, se pueden configurar diferentes estilos según diferentes situaciones.

  • Atributos:

    1. activeClassName : la clase cuando el enlace de cadena está activo
    2. activeStyle - el estilo del objeto cuando el enlace está activo
    3. isActive —— función, que puede determinar dinámicamente si el enlace está activo
    4. estilo —— función, estilo establecido dinámicamente
    5. className —— función, establece dinámicamente el valor de la clase

Método de entrega de ruta

  • Método 1: a través de un componente
import Home from "./Home";
<Route  path="/student/:id" component={
    
    Home}/>
  • Método 2 - a través de render
    • render necesita pasar una función de devolución de llamada, la función de devolución de llamada tiene un parámetro routeProps y el valor devuelto es el componente que se va a representar
import Home from "./Home";

//这样子就可以传递数据了,但是不会自动传递match,location,history了
<Route  path="/student/:id" render = {
    
     () => <Home/> } />

//如果需要match.location,history
<Route path="/student/:id" render={
    
    (routeProps) => <Home {
    
    ...routeProps}/>} />
  • Método 3: especifique el componente montado a través de elementos secundarios

    • Uso 1: cuando los niños configuran una función de devolución de llamada , es similar a renderizar. Se puede escribir de la misma manera. Cuando, el componente se montará independientemente de si la ruta coincide o no.
    <Route path="/student/:id" children={
          
          (routeProps) => <Home {
          
          ...routeProps}/>}/> 
        
    //浏览器
        访问/  加载home
        访问/abc 加载home
    
    • Uso 2: pase un jsx y cárguelo solo cuando la ruta coincida

      • Cuando es imposible pasar coincidencia, ubicación, historial; pero se puede resolver con una función de gancho
      const match = useRouteMatch()
      const location = useLocation();
      const history = useHistory();
      
      const params = useParmas();//获取params参数
      
    <Route path="/student/:id" children={
          
           <Home}/>}/> 
    
  • Método 4 - a través de prop.children

    • La base es que cuando la ruta no coincida, aún se montará
<Route path="/student/:id">
	<Home/>
</Route>

//如果需要match location history
<Route path="/student/:id">
	{
    
    
		routeProps => <Home {
    
    ...routeProps}/>
	}
</Route>
  • Los tres atributos pasados
match:{
    
    
    isExact 检查路径是否完全匹配
    params: {
    
    } (默认空对象)请求的参数
    path:设置的path属性 比如/student /student/:id
    url: 真实的路径比如/student /student/4
} 匹配的信息
		
location: {
    
    
    has:
    key:请求的id
    search:(默认undefined)查询的字符串比如/student?name=admin
    state:(默认undefined)

}地址信息
		
history: {
    
    
    go自由跳转(方法)
    goBack向后条(方法)
    goForwar向前跳(方法)
    push:历史记录添加一条新页面并跳转(方法)
    replace:替换记录并跳转(方法),跳转可以传递state
    replace({
    
    pathname:'/student/2,state:{
    
    name:李白},)
}控制页面的跳转

Anidamiento de rutas

<Route path="/about">
	<About/>
	<Route path="/about/hello">
		<Hello/>
	</Route>
</Route>

Componente rápido

  • confirmación de salto
  • Por ejemplo, se ingresa al formulario y luego el usuario quiere saltar a otra página. En este momento, podemos usar Promptel componente para preguntarle al usuario si desea saltar.
  • messageInformación de solicitud de configuración de propiedad
  • whenLa propiedad se repite solo cuando es verdadera y el valor predeterminado estrue
  • El siguiente ejemplo, cuando la entrada tiene un valor, le preguntará al usuario si saltar
import React, {useState} from 'react';
import {Prompt} from "react-router-dom"
const MyForm = () => {
    const [value,setValue] = useState('');
    const [isPrompt,setPrompt] = useState(false);
    const handleOnChange = (e) => {
        setValue(e.target.value);
        setPrompt(!!e.target.value.trim().length);
    }
    return (
        <div>
            <Prompt message={'确定离开当前页面吗'} when={isPrompt}/>
            <p>form组件</p>
            <input type='text' value={value} onChange={handleOnChange}/>
        </div>
    );
};
export default MyForm;

redirigir

  • función de redireccionamiento

  • Atributos

    • to
    • from
    • replace(El método de reemplazo predeterminado es reemplazar)
    • push
    //当访问abc的时候,自动重定向到form
    <Redirect from = "/abc" to="/form"/>
    
  • Si es así Switch, se puede implementar la función de redirección

<Switch>
    <Route path="/about" component={About}/>
	<Route path="/home" component={Home}/>
    //当上面的都匹配不到的时候,就会匹配Redirect组件当中的to值
	<Redirect to="/home"/>
</Switch>

reaccionar-enrutador-dom@6

  • RouteEn la versión de enrutamiento de reacción @ 6, debemos envolver una capa fuera de todos Routes, función y Switchtipo, solo uno Routesde ellos Routecoincidirá

  • RouteYa no se usan propiedades component, sino elementpropiedades

  • NavLink

  • useParams, useLocationsin cambios

  • useMatchCompruebe si la URL actual coincide con una ruta (tenga en cuenta que es una ruta, no una ruta, como una ruta es /student/:id, no /student/1)

    • coincidencia: devolver un objeto
    • Ninguna coincidencia devuelve nulo
  • useHistoryEliminar, usar useNavigateen su lugar, obtener la función utilizada para saltar a la página

    • const nav = useNavigate();nav('/home')跳转到/homeMétodo de inserción predeterminado
    • nav('/about',{replace:true}), usa replace para saltar
  • Navigateetiqueta añadida

pasar parámetros

  • useParamsEl parámetro params se puede obtener
import React from 'react';
import {useParams} from "react-router-dom";

const STATIC_DATA = [
    {id:1,name:'傻瓜超人'},
    {id:2,name:'西瓜超人'},
    {id:3,name:'动感超人'},
    {id:4,name:'酸梅超人'},
]
const About = () => {
    const {id} = useParams();
    //不要忘记转化为了数字
    const findData = STATIC_DATA.find(item => item.id === id*1)
    console.log(findData)
    return (
        <div>
            <h2>超人的类型</h2>
            <h3>{findData.id} --- {findData.name}</h3>
        </div>
    );
};

export default About;

useNavegar(gancho)

  • Puede obtener una función para el salto de página
import React from 'react';
import {useNavigate} from "react-router-dom"
const Home = () => {
    const router = useNavigate()
    const handleClick = () => {
        //使用replace模式(默认不设置是push模式)
        router('/about/1',{
            replace:true,
        })
    }
    return (
        <div>
            我是精彩的主页
            <button onClick={handleClick}>跳转到about页面</button>
        </div>
    );
};

export default Home;

Anidamiento de enrutamiento y marcadores de posición de salida

<Routes>
    <Route path={'/home'} element={<Home/>}>
    	<Route path={'page'} element={<HomePage/>}/>
    </Route>
    <Route path={'/about/:id'} element={<About/>}> </Route>
</Routes>

Home.jsx usa el marcador de posición Outlet

import React from 'react';
import {useNavigate,Outlet} from "react-router-dom"
const Home = () => {
    const router = useNavigate()
    const handleClick = () => {
        //使用replace模式(默认不设置是push模式)
        router('/about/1',{
            replace:true,
        })
    }
    return (
        <div>
            我是精彩的主页
            <button onClick={handleClick}>跳转到about页面</button>
            <Outlet/>
        </div>
    );
};

export default Home;

  • OutletIndica el componente de la ruta anidada. Cuando la ruta en la ruta anidada se empareja correctamente, el Outlet representa el componente de la ruta anidada. Si la coincidencia no es correcta, el Outlet no es nada

Componente de navegación

  • Siempre que se procese Navigate , la vista cambiará

  • replaceEl atributo se usa para controlar el modo de salto (presionar o reemplazar, el valor predeterminado es presionar)

Enlace de navegación

  • El camino de className
//css内容
.active_style{
    background-color: red;
}


//jsx内容
import React from 'react';
import {Link,NavLink} from "react-router-dom";
import "./test.css";
const Menu = () => {
    const activeStyle = ({isActive}) => {
        return isActive ? 'active_style' : null;
    }
    return (
        <div>
            <Link to={'/home'}>Home页面</Link>
            {/*<NavLink to='/home/page' className={activeStyle}>Home页面下的page</NavLink>*/}
            {/*或者*/}
            <NavLink to={'/home/page'} className={
                ({isActive}) => {
                    return isActive ? 'active_style' : null;
                }
            }>Home页面下的page</NavLink>
            <Link to={'/about'}>关于页面</Link>
        </div>
    );
};

export default Menu;

//激活的时候HTML状态
<a href="/home/page" aria-current="page" class="active_style">Home页面下的page</a>
//未激活的时候HTML状态
<a href="/home/page">Home页面下的page</a>
  • el camino del estilo
import React from 'react';
import {Link,NavLink} from "react-router-dom";
const Menu = () => {
    const activeStyle = ({isActive}) => {
        return isActive ? {backgroundColor:'red'} : null;
    }
    return (
        <div>
            <Link to={'/home'}>Home页面</Link>
            {/*<NavLink to='/home/page' style={*/}
            {/*    ({isActive}) => {*/}
            {/*        return isActive ? {backgroundColor:'red'} : null*/}
            {/*    }*/}
            {/*}>Home页面下的page</NavLink>*/}
            <NavLink to='/home/page' style={activeStyle}>Home页面下的page</NavLink>
            <Link to={'/about'}>关于页面</Link>
        </div>
    );
};

export default Menu;

//激活的时候HTML状态
<a aria-current="page" class="active" href="/home/page" style="background-color: red;">Home页面下的page</a>

//未激活的时候HTML状态
<a class="" href="/home/page" style="">Home页面下的page</a>

otros ganchos

usar nota

  • Puede almacenar todo en caché, similar a useEffect, useCallback
  • useCallback se usa para almacenar en caché los objetos de funciones y useMemo para almacenar en caché los resultados de ejecución de funciones

React.forwardRef和useImperativeHandle

  • React.forwardRef puede exponer el objeto ref al exterior (por ejemplo, el cuadro de entrada en el componente B se puede operar a través de ref en el componente A (un DOM completo))

  • Y useImplerativeHandle puede especificar el objeto de referencia expuesto al exterior (por ejemplo, un componente puede manipular el valor del cuadro de entrada de entrada en el componente B a través de la referencia (solo se puede manipular el valor de este cuadro de entrada))

  • refEchemos un vistazo primero, ¿qué sucede si vincula un componente personalizado directamente?

    • El resultado es obvio, React solicita Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?y, al mismo tiempo, la referencia de salida no puede obtener múltiples objetos dom, y React no sabe a quién darle
    • Debido a que es imposible obtener directamente el objeto dom del componente de reacción, porque un componente de reacción puede contener

Componente App.jsx

import React,{useState,useRef} from 'react';
import Some from "./component/Some";
const App = () => {
    const [count,setCount] = useState(0);
    const someRef = useRef();
    const handleShow = () => {
        console.log(someRef.current)
    }
    return (
        <div>
            <p>总数:{count}</p>
            <button onClick={() => setCount(prevState => prevState + 1)}>点击加1</button>
            <button onClick={handleShow}>查看Some的ref的值</button>
            <Some ref={someRef}/>
        </div>
    );
};

export default App;

Componente Some.jsx

import React,{useRef} from 'react';

const Some = () => {
    const inputRef = useRef();
    const handleBtn = () => {
        console.log(inputRef.current.value);
    }
    return (
        <div>
            我是Some组件
            <input ref={inputRef}/>
            <button onClick={handleBtn}>获取input的值</button>
        </div>
    );
};

export default Some;

  • Así que necesitamos usarlo React.forwardRefen el componente personalizado Algunos

Componente Some.jsx

import React,{useRef,forwardRef} from 'react';

const Some = forwardRef((props,ref) => {
    const inputRef = useRef();
    const handleBtn = () => {
        console.log(inputRef.current.value);
    }
    return (
        <div>
            <p ref={ref}>我是Some组件</p>
            <input ref={inputRef}/>
            <button onClick={handleBtn}>获取input的值</button>
        </div>
    );
})

export default Some;

Componente App.jsx

import React,{useState,useRef} from 'react';
import Some from "./component/Some";
const App = () => {
    const [count,setCount] = useState(0);
    const someRef = useRef();
    const handleShow = () => {
        console.log(someRef)
    }
    return (
        <div>
            <p>总数:{count}</p>
            <button onClick={() => setCount(prevState => prevState + 1)}>点击加1</button>
            <button onClick={handleShow}>查看Some的ref的值</button>
            <Some ref={someRef}/>
        </div>
    );
};

export default App;

  • Pero no es seguro usarlo así forwardRef, porque puede manipular directamente la entrada del componente Some en el componente externo y obtener la entrada por completo, lo que equivale a dar el diamante al ejemplo para medir el tamaño de un diamante, para que podamos usarlo useImperativeHandle, solo dígale algunas propiedades del "diamante"

Aplicación.jsx

import React,{useState,useRef} from 'react';
import Some from "./component/Some";
const App = () => {
    const [count,setCount] = useState(0);
    const someRef = useRef();
    const handleShow = () => {
        const temp = count + 1;
        setCount(temp);
        /*设置Some组件当中的p标签的内容*/
        someRef.current.setContext(temp)
    }
    return (
        <div>
            <p>总数:{count}</p>
            <button onClick={handleShow}>点击加1并设置Some组件P的值</button>
            <Some ref={someRef}/>
        </div>
    );
};

export default App;

Algunos.jsx

import React,{useRef,forwardRef,useImperativeHandle} from 'react';

const Some = forwardRef((props,ref) => {
    const inputRef = useRef();
    const pRef = useRef()
    const handleBtn = () => {
        console.log(inputRef.current.value);
    }
    useImperativeHandle(ref,() => {
        /*返回值将作为App.jsx组件获取到的ref的值*/
        return {
            setContext(content){
                pRef.current.textContent = content;
            }
        }
    })
    return (
        <div>
            <p ref={pRef}>我是Some组件</p>
            <input ref={inputRef}/>
            <button onClick={handleBtn}>获取input的值</button>
        </div>
    );
})

export default Some;

representaciones

useEffect y useInsertionEffect y useLayoutEffect

  • Orden de ejecución del primero al último useInsertionEffect>>>useLayoutEffectuseEffect
  • Cuando use useEffectflashes, considere usar los otros dos

usarValorDeferido

  • Cuando tenemos múltiples componentes que usan el mismo estado, los componentes pueden afectarse entre sí y un componente se atascará, lo que hará que todos los componentes se atasquen.

notas rotas

  • La primera letra del componente debe estar en mayúscula

  • React quiere cancelar el comportamiento predeterminado, puede usar el objeto de evento, como event.preventDefault() para cancelar el comportamiento predeterminado, event.stopPropagation() para cancelar el burbujeo del evento

  • los accesorios son de solo lectura y no pueden modificar el atributo props

  • toLocaleString()

    • No esperaba que tantos usaran este método

  • componente de función rsc sin accesorios

  • componente de función rsi con accesorios

  • componentes de clase rcc

  • props.children representa el cuerpo de la etiqueta de la función

  • La función de gancho en React solo se puede llamar en el componente de función o en el gancho personalizado (se usa directamente en el componente de función

function App() {
    
    
	function fn(){
    
    
		usexxxxx,//错误,这不叫函数组件调用,而是在函数组件的内部函数调用了,错误
	} 
}
  • En la función de flecha definida directamente en la clase, esto siempre apunta al objeto de instancia
class MyClass {
    
    
	fn = () => {
    
    
		//在类中直接定义的箭头函数,this永远都指向实例对象
	}
	
	fn2(){
    
    
		const fn3 = () => {
    
    
			//这都不是在类中直接定义的箭头函数了,肯定不是指向实例对象
		}
	}
}
  • ¿Por qué no se pueden usar los hipervínculos para lograr saltos de enrutamiento al enrutar?
    • Hará que se envíe una solicitud al servidor para recargar la página, y si el servidor no realiza una operación de salto, se BrowserRouterproducirá un 404 en el modo a través de un salto de enlace (porque esta solicitud no es procesada por react-router), así que para evitar Hay dos soluciones a esta situación
      1. El uso del servidor HashRouter no procesará las cosas detrás de #, solicitamos localhost/#/about, el servidor solo procesará localhost, no /about
      2. Si aún necesita usar BrowserRouter, debe modificar la configuración del servidor y reenviar todas las solicitudes a index.html
//错误写法
<a href="/">跳转到主页</a>
<a href="/about">跳转到关于页</a>
  • fetchBaseQueryEstablecer el encabezado de la solicitud y leer el valor del estado
const studentApi = createApi({
    
    
    // ...
    baseQuery: fetchBaseQuery({
    
    
        baseUrl: "http://localhost:1337/api",
        prepareHeaders: (headers,abc) => {
    
    
            console.log(abc);
            //输出
            	endpoint: "getStudents"
                extra: undefined
                forced: false
                getState: ƒ a()
                type: "query"
            return headers;
        }
    })
	// ...
});


const studentApi = createApi({
    
    
    // ...
    baseQuery: fetchBaseQuery({
    
    
        baseUrl: "http://localhost:1337/api",
        prepareHeaders: (headers, {
     
     getState}) => {
    
    
            const token = getState().auth.token;
            return headers;
        }
    }),
	// ...
});
  • Los ganchos solo se pueden usar en componentes React y ganchos personalizados
    • Los ganchos no se pueden usar en funciones anidadas u otras declaraciones (if, switch, for), etc.

Guess you like

Origin blog.csdn.net/u014582342/article/details/128844458