從零到專家。用西班牙語的詳細答案? ?
如果您喜歡這個項目,請留下您的。
Twitch編程流:Twitch.tv/midudev
Discord開發社區:discord.gg/midudev
children本身是什麼反應?class ?useState做什麼?useEffect做什麼?useEffect的使用案例useEffect中的事件useId做什麼?useEffect ?useEffect中API的請求useEffect和useLayoutEffect有什麼區別?if ?index用作React列表中的密鑰是不好的做法?useMemo用途是什麼?useMemo來優化我們的組件是一個好主意嗎?useCallback用途是什麼?useCallback優化我們的組件是一個好主意嗎?useCallback和useMemo有什麼區別?useRef如何工作?useLayoutEffect做什麼?useLayoutEffect執行順序StrictMode是什麼?SyntheticEvent是什麼?flushSync是什麼?useImperativeHandle胡克手有什麼用?cloneElement方法是什麼?StrictMode渲染應用程序兩倍?useEffect中止提取請願書?useDebugValue是什麼?Profiler ?renderToStaticNodeStream()和renderToPipeableStream()有什麼區別?useDeferredValue鉤?renderToReadableStream()方法是什麼?React是一個開源JavaScript庫,用於構建用戶界面。它基於UI的化合物:接口分為包含其自己狀態的獨立組件。當組件的狀態發生變化時,React再次渲染接口。
這使得一個非常有用的工具可以構建複雜的接口,因為它可以將接口分為較小且可重複使用的零件。
它是由喬丹·沃爾克(Jordan Walke)於2011年創建的。
這是一個非常受歡迎的圖書館,被許多公司(例如Facebook,Netflix,Airbnb,Twitter,Instagram)使用。
興趣鏈接:
⬆回到索引
反應的主要特徵是:
組件:REACT基於UI的化合物。該界面分為包含其自己狀態的獨立組件。當組件的狀態發生變化時,React再次渲染接口。
虛擬DOM :React使用虛擬DOM渲染組件。虛擬DOM是真正的DOM表示。當組件的狀態發生變化時,React再次渲染接口。 React不是修改實際DOM,而是修改虛擬DOM,然後將虛擬DOM與真實DOM進行比較。通過這種方式,React知道應該將哪些更改應用於真實的DOM。
聲明性:反應是聲明性的,這意味著未指定應該如何執行任務,而是應該做什麼。這使代碼更易於理解和維護。
單向:反應是單向的,這意味著數據沿一個方向流動。數據從父母流向孩子的組成部分。
通用:React可以在客戶端和服務器中執行。此外,您可以使用React Antial來為Android和iOS創建本機應用程序。
⬆回到索引
我們不會告訴您如何根據指令渲染界面。我們告訴您渲染和反應是造成它的原因。
聲明和當務之急之間的一個例子:
// Declarativo
const element = < h1 > Hello, world </ h1 >
// Imperativo
const element = document . createElement ( 'h1' )
element . innerHTML = 'Hello, world'⬆回到索引
組件是呈現一部分接口的代碼。組件可以進行參數化,重複使用並可以包含其自己的狀態。
在React中,組件是使用函數或類創建的。
⬆回到索引
React使用JSX聲明應呈現的內容。 JSX是JavaScript的擴展名,可直觀地向HTML編寫更接近的代碼,從而提高了代碼的可讀性並使其更容易理解。
如果沒有JSX,我們應該使用React.createElement以這種方式手動創建接口的元素:
import { createElement } from 'react'
function Hello ( ) { // un componente es una función! ?
return React . createElement (
'h1' , // elemento a renderizar
null , // atributos del elemento
'Hola Mundo ?!' // contenido del elemento
)
}這非常乏味,鮮明。因此,React使用JSX聲明應該呈現的內容。這就是為什麼我們以這種方式使用JSX:
function Hello ( ) {
return < h1 > Hola Mundo ?! </ h1 >
}這兩個代碼都是等效的。
⬆回到索引
JSX使用轉運器或編譯器將JSX轉換為瀏覽器中兼容的JavaScript代碼。今天,最著名的是Babel,它使用一系列插件與轉換兼容,但是還有其他插件。
您可以看到如何將JSX轉變為Babel Code Playground。
在某些特殊情況下,不需要轉介劑。例如,您對JSX語法具有本機支持,並且不必轉換代碼以使其兼容。
⬆回到索引
組件是接收道具並返回元素的函數或類。元素是代表DOM節點或React組件的實例的對象。
// Elemento que representa un nodo del DOM
{
type : 'button' ,
props : {
className : 'button button-blue' ,
children : {
type : 'b' ,
props : {
children : 'OK!'
}
}
}
}
// Elemento que representa una instancia de un componente
{
type : Button ,
props : {
color : 'blue' ,
children : 'OK!'
}
}⬆回到索引
React中的組件是返回React元素的函數或類。如今,最建議的是使用功能:
function HelloWorld ( ) {
return < h1 > Hello World! </ h1 >
}但是您也可以使用類創建一個反應組件:
import { Component } from 'react'
class HelloWorld extends Component {
render ( ) {
return < h1 > Hello World! </ h1 >
}
}重要的是,功能或類的名稱始於大寫字母。這是區分組件和HTML元素的反應所必需的。
⬆回到索引
道具是組件的屬性。它們是從一個組件傳遞到另一個組件的數據。例如,如果您的Button組件顯示一個按鈕,則可以傳遞text ,以便該按鈕顯示該文本:
function Button ( props ) {
return < button > { props . text } </ button >
}我們可以理解Button組件是一個通用按鈕,並且text是按鈕中顯示的文本。因此,我們正在創建一個可重複使用的組件。
還應考慮到,在JSX中使用任何JavaScript表達式時,您必須用{}將它們包裹起來,在這種情況下為props對象,否則JSX將其視為平面文本。
要使用它,我們指示組件的名稱,並通過我們想要的道具:
< Button text = "Haz clic aquí" />
< Button text = "Seguir a @midudev" / >道具是一種像使用功能一樣對組件進行參數化的方法。我們可以將任何類型的數據傳遞給組件,甚至其他組件。
⬆回到索引
children本身是什麼反應? children本身是傳遞給組件的特殊特殊之處。它是包含圍繞組件的元素的對象。
例如,如果我們有一個帶有標題和內容的卡片Card組件,我們可以使用children顯示內容:
function Card ( props ) {
return (
< div className = "card" >
< h2 > { props . title } </ h2 >
< div > { props . children } </ div >
</ div >
)
}然後,我們可以按以下方式使用它:
< Card title = "Título de la tarjeta" >
< p > Contenido de la tarjeta </ p >
</ Card >在這種情況下, children自己包含<p>Contenido de la tarjeta</p> 。
知道並知道如何使用children本身對於在React中創建可重複使用的組件非常重要。
⬆回到索引
道具是一個將父親組成部分的參數傳遞給子女組成部分的對象。它們是不變的,不能從子女組成部分中修改。
狀態是組件中定義的值。它的值是不可變的(不能直接修改),但是可以建立狀態的新值,從而對組件進行反應。
因此,與此同時,狀態會影響組件的渲染,其管理層不同。
⬆回到索引
是的,可以用道具的價值初始化狀態。但是必須記住的是,如果自己的變化,狀態將不會自動更新。這是因為當組件首次安裝時,狀態是一次初始化的。
例如,使用功能組件:
const Counter = ( ) => {
const [ count , setCount ] = useState ( 0 )
return (
< div >
< Count count = { count } />
< button onClick = { ( ) => setCount ( count + 1 ) } > Aumentar </ button >
</ div >
)
}
const Count = ( { count } ) => {
const [ number , setNumber ] = useState ( count )
return < p > { number } </ p >
}在這種情況下, Count組件以其count的價值初始化其狀態。但是,如果其count ,則狀態將不會自動更新。因此,單擊時,我們將始終在屏幕上看到數字0。
在此示例中,最好簡單地在Count組件中使用count ,因此它將始終呈現。
這是一種很好的做法,可以最大化我們組件的狀態,只要您可以計算出從道具顯示的值即可。
在您需要用道具初始化狀態的情況下,最好將initial前綴添加到道具中,以表明它是狀態的初始值,然後我們將不再使用它:
const Input = ( { initialValue } ) => {
const [ value , setValue ] = useState ( initialValue )
return (
< input
value = { value }
onChange = { e => setValue ( e . target . value ) }
/>
)
}認為國家會更新是一個非常普遍的錯誤,因此請考慮一下。
⬆回到索引
條件渲染是根據條件顯示一個或另一個組件的方法。
為了使有條件的渲染在React中,我們使用三元運營商:
function Button ( { text } ) {
return text
? < button > { text } </ button >
: null
}在這種情況下,如果text存在,則呈現按鈕。如果不存在,則沒有任何渲染。
通常,使用類型的&&運算符找到有條件渲染的實現:
function List ( { listArray } ) {
return listArray ?. length && listArray . map ( item => item )
}看來這是有道理的...如果length為正(更大至零),我們會繪製地圖。 !好吧!請小心,如果您的length為零,因為它將在瀏覽器A 0中繪製。
最好使用三元運算符。肯特·多德斯(Kent C. Dodds)有一篇有趣的文章談論這個主題。在JSX中使用而不是&&
⬆回到索引
class ?要將CSS類應用於React中的組件,我們使用className :
function Button ( { text } ) {
return (
< button className = "button" >
{ text }
</ button >
)
}稱為className的原因是因為class是JavaScript中保留的單詞。因此,在JSX中,我們必須使用className應用CSS類。
⬆回到索引
要將在線CSS樣式應用於React中的組件,我們使用style 。我們將如何使用HTML進行操作的區別在於,在React中,樣式是作為一個對象而不是文本鏈傳遞的(這可以用雙方平方括號更清楚,這是第一個表明它是JavaScript表達式,而創建對象的幾秒鐘):
function Button ( { text } ) {
return (
< button style = { { color : 'red' , borderRadius : '2px' } } >
{ text }
</ button >
)
}請注意,此外,CSS屬性的名稱在駱駝中。
⬆回到索引
您可以使用style和三元操作員使用React中的組件應用樣式:
function Button ( { text , primary } ) {
return (
< button style = { { color : primary ? 'red' : 'blue' } } >
{ text }
</ button >
)
}在上一種情況下,如果primary是true ,則按鈕將帶有紅色。如果沒有,它將具有藍色。
您也可以使用類遵循相同的機制。在這種情況下,我們使用三元操作員來決定是否添加類:
function Button ( { text , primary } ) {
return (
< button className = { primary ? 'button-primary' : '' } >
{ text }
</ button >
)
}我們還可以使用諸如classnames之類的庫:
import classnames from 'classnames'
function Button ( { text , primary } ) {
return (
< button className = { classnames ( 'button' , { primary } ) } >
{ text }
</ button >
)
}在這種情況下,如果primary primary是true ,則將添加到按鈕中。如果沒有,它將不會添加。相反,將始終添加button類。
⬆回到索引
列表渲染是迭代一系列元素和為每個元素渲染的反應元素的方式。
為了在React中列出渲染列表,我們使用數組的map方法:
function List ( { items } ) {
return (
< ul >
{ items . map ( item => (
< li key = { item . id } > { item . name } </ li >
) ) }
</ ul >
)
}在這種情況下,使用List組件呈現元素列表。 List組件接收其自己的items ,該項目是類型[{ id: 1, name: "John Doe" }]的對像數組。 List組件為數組的每個元素渲染一個li元素。
li元素有一個自己的key ,是每個元素的唯一標識符。這是對識別列表的每個元素並有效更新列表所必需的。後來,對此有了更詳細的解釋。
⬆回到索引
如果您要在組件的渲染之外寫評論,則可以使用JavaScript註釋語法而毫無問題:
function Button ( { text } ) {
// Esto es un comentario
/* Esto es un comentario
de varias líneas */
return (
< button >
{ text }
</ button >
)
}如果要在組件的渲染中寫評論,則必須在鍵上包裝註釋,並始終使用塊註釋語法:
function Button ( { text } ) {
return (
< button >
{ /* Esto es un comentario en el render */ }
{ text }
</ button >
)
}⬆回到索引
為了將事件添加到React中的組件中,我們使用Camelcase中本機瀏覽器事件的on和名稱:
function Button ( { text , onClick } ) {
return (
< button onClick = { onClick } >
{ text }
</ button >
)
}在這種情況下, Button組件會收到一個函數的onClick 。當用戶單擊按鈕時,執行了onClick功能。
⬆回到索引
要將參數傳遞到管理React中事件的函數,我們可以使用匿名函數:
function Button ( { id , text , onClick } ) {
return (
< button onClick = { ( ) => onClick ( id ) } >
{ text }
</ button >
)
}當用戶單擊按鈕時,通過傳遞id的值來執行onClick函數。
您還可以通過傳遞id的值來創建一個運行onClick函數的函數:
function Button ( { id , text , onClick } ) {
const handleClick = ( event ) => { // handleClick recibe el evento original
onClick ( id )
}
return (
< button onClick = { handleClick } >
{ text }
</ button >
)
}⬆回到索引
狀態是包含可以隨時間變化的數據的對象。在React中,狀態用於控制接口中的變化。
要了解這個概念,請考慮一個房間開關。這些開關通常具有兩個狀態:開關。當我們激活開關並將其on時,然後燈打開,當我們將其off時,燈會關閉。
同樣的概念可以應用於用戶界面。例如,我喜歡Facebook的按鈕,當用戶給出時,我會擁有meGusta的true ,而當他沒有這樣做時,我喜歡false 。
我們不僅可以在州布爾值中擁有對象,數組,數字等。
例如,如果您有一個顯示計數器的Counter組件,則可以使用狀態控制計數器的值。
為了在React中創建一個狀態,我們使用掛鉤useState :
import { useState } from 'react'
function Counter ( ) {
const [ count , setCount ] = useState ( 0 )
return (
< div >
< p > Contador: { count } </ p >
< button onClick = { ( ) => setCount ( count + 1 ) } > Aumentar </ button >
</ div >
)
}使用掛鉤useState時,這將返回一個兩個位置array :
破壞通常用於促進閱讀和保存一些代碼。另一方面,當將事實作為參數傳遞給useState ,我們會指示您的初始狀態。
使用類別的組成部分,國家的創建就是這樣:
import { Component } from 'react'
class Counter extends Component {
constructor ( props ) {
super ( props )
this . state = { count : 0 }
}
render ( ) {
return (
< div >
< p > Contador: { this . state . count } </ p >
< button onClick = { ( ) => this . setState ( { count : this . state . count + 1 } ) } >
Aumentar
</ button >
</ div >
)
}
}⬆回到索引
鉤子是一種反應API,它使我們能夠在用功能創建的組件中具有狀態和其他反應特性。
以前,這是不可能的,並迫使我們創建了一個class組件來訪問書店的所有可能性。
鉤子是一個鉤子,正是他們所做的是,它們允許您將功能性組件鉤到React提供的所有特徵上。
⬆回到索引
useState做什麼?掛鉤useState用於創建狀態變量,這意味著其值是動態的,這可以隨著時間的推移而改變,並且需要重新領先組件的使用。
接收參數:
返回一個帶有兩個變量的數組:
setIsOpen(isOpen => !isOpen)在此示例中,我們顯示瞭如何在0中初始初始化count數值,並且每次在按鈕onClick事件中使用setCount函數修改值時,它也會呈現。
import { useState } from 'react'
function Counter ( ) {
const [ count , setCount ] = useState ( 0 )
return (
< >
< p > Contador: { count } </ p >
< button onClick = { ( ) => setCount ( count => count + 1 ) } > Aumentar </ button >
</ >
)
}⬆回到索引
當幾個組件需要共享相同的狀態數據時,建議將這種狀態提高到其最接近的共同祖先。
否則說。如果兩個孩子共享與父親相同的數據,那麼該州就會搬到父親,而不是在子女中維持當地國家。
要理解它,最好的是我們以一個例子看到了它。想像一下,我們有一個理想的禮物清單,我們想添加禮物並在列表中顯示總禮物。
import { useState } from 'react'
export default function App ( ) {
return (
< >
< h1 > Lista de regalos </ h1 >
< GiftList />
< TotalGifts />
</ >
)
}
function GiftList ( ) {
const [ gifts , setGifts ] = useState ( [ ] )
const addGift = ( ) => {
const newGift = prompt ( '¿Qué regalo quieres añadir?' )
setGifts ( [ ... gifts , newGift ] )
}
return (
< >
< h2 > Regalos </ h2 >
< ul >
{ gifts . map ( gift => (
< li key = { gift } > { gift } </ li >
) ) }
</ ul >
< button onClick = { addGift } > Añadir regalo </ button >
</ >
)
}
function TotalGifts ( ) {
const [ totalGifts , setTotalGifts ] = useState ( 0 )
return (
< >
< h2 > Total de regalos </ h2 >
< p > { totalGifts } </ p >
</ >
)
}如果我們希望每次添加禮物時都會更新禮物,會發生什麼?如我們所見,這是不可能的,因為totalGifts狀態在TotalGifts組件中,而不是GiftList組件中。而且,由於我們無法從TotalGifts訪問GiftList的狀態,因此當我們添加禮物時,我們將無法更新totalGifts的狀態。
我們必須將gifts狀態上傳到父親App中,我們將把禮物(例如道具)的數量傳遞給TotalGifts組成部分。
import { useState } from 'react'
export default function App ( ) {
const [ gifts , setGifts ] = useState ( [ ] )
const addGift = ( ) => {
const newGift = prompt ( '¿Qué regalo quieres añadir?' )
setGifts ( [ ... gifts , newGift ] )
}
return (
< >
< h1 > Lista de regalos </ h1 >
< GiftList gifts = { gifts } addGift = { addGift } />
< TotalGifts totalGifts = { gifts . length } />
</ >
)
}
function GiftList ( { gifts , addGift } ) {
return (
< >
< h2 > Regalos </ h2 >
< ul >
{ gifts . map ( gift => (
< li key = { gift } > { gift } </ li >
) ) }
</ ul >
< button onClick = { addGift } > Añadir regalo </ button >
</ >
)
}
function TotalGifts ( { totalGifts } ) {
return (
< >
< h2 > Total de regalos </ h2 >
< p > { totalGifts } </ p >
</ >
)
}這樣,我們所做的就是提高國家。我們已經將其從GiftList組件移至App組件。現在,我們將禮物傳遞給GiftList組件和一種更新狀態的方式,並且我們也支持TotalGifts禮物的總數。
⬆回到索引
useEffect做什麼?當組件的渲染或效果變化時,使用掛鉤useEffect用於執行代碼。
接收兩個參數:
在此示例中,我們在組件加載時在控制台中顯示一條消息,並且每次count數值都會改變:
import { useEffect , useState } from 'react'
function Counter ( ) {
const [ count , setCount ] = useState ( 0 )
useEffect ( ( ) => {
console . log ( 'El contador se ha actualizado' )
} , [ count ] )
return (
< >
< p > Contador: { count } </ p >
< button onClick = { ( ) => setCount ( count + 1 ) } > Aumentar </ button >
</ >
)
}⬆回到索引
useEffect的使用案例我們可以以不同的方式使用Hook useEffect ,例如:
resize以了解用戶何時更改窗口大小。⬆回到索引
useEffect中的事件在useEffect中,我們可以訂閱瀏覽器事件,因為該事件的resize要知道用戶何時更改窗口大小。當組件被拆卸以避免內存洩漏時,我們必須將其不注重。為此,我們必須在拆卸組件時將返回useEffect中的功能。
import { useEffect } from 'react'
function Window ( ) {
useEffect ( ( ) => {
const handleResize = ( ) => {
console . log ( 'La ventana se ha redimensionado' )
}
window . addEventListener ( 'resize' , handleResize )
return ( ) => {
window . removeEventListener ( 'resize' , handleResize )
}
} , [ ] )
return (
< p > Abre la consola y redimensiona la ventana </ p >
)
}⬆回到索引
useId做什麼? useId是生成唯一標識符的鉤子,可以傳遞給HTML標籤的屬性,對於可訪問性特別有用。
在組件的上層處的Llama useId生成唯一的ID:
import { useId } from 'react'
function PasswordField ( ) {
const passwordHintId = useId ( )
// ...接下來,生成不同屬性的ID通過:
< >
< input type = "password" aria-describedby = { passwordHintId } />
< p id = { passwordHintId } >
</ > aria-describedby標籤使您可以指定兩個標籤相互關聯,它可以與Useid生成唯一的標識,即使PasswordField出現在屏幕上幾次,生成的標識也不會碰撞。
完整的示例就是這樣:
import { useId } from 'react'
function PasswordField ( ) {
const passwordHintId = useId ( )
return (
< >
< label >
Password:
< input
type = "password"
aria-describedby = { passwordHintId }
/>
</ label >
< p id = { passwordHintId } >
El password debe ser de 18 letras y contener caracteres especiales
</ p >
</ >
)
}
export default function App ( ) {
return (
< >
< h2 > Choose password </ h2 >
< PasswordField />
< h2 > Confirm password </ h2 >
< PasswordField />
</ >
)
}如您在App中所見,我們正在使用兩次組件。如果我們手工放置ID,例如password ,那麼ID將不會是唯一的,並且將被重複。這就是為什麼重要的是要使用useId自動生成ID。
⬆回到索引
當使用Hook useEffect安裝組件時,我們可以執行代碼,而無需通過任何依賴。在這種情況下,當組件掛載時將執行作為第一個參數的函數。
import { useEffect } from 'react'
function Component ( ) {
useEffect ( ( ) => {
console . log ( 'El componente se ha montado' )
} , [ ] )
return (
< p > Abre la consola y re-dimensiona la ventana </ p >
)
}⬆回到索引
片段是對元素進行分組的一種方法,而無需向DOM添加額外的元素,因為React不允許在組件中返回幾個元素,而只能返回根部元素。
為了在React中創建片段,我們使用Fragment組件:
import { Fragment } from 'react'
function App ( ) {
return (
< Fragment >
< h1 > Titulo </ h1 >
< p > Párrafo </ p >
</ Fragment >
)
}我們還可以使用縮寫語法:
function App ( ) {
return (
< >
< h1 > Titulo </ h1 >
< p > Párrafo </ p >
</ >
)
}⬆回到索引
建議使用片段而不是div時,包裝幾個元素的原因是:
div向DOM添加了一個額外的元素,而片段編號則添加了。這使得HTML元素的數量和DOM較低的深度。div則可能會在元素對齊中遇到問題。div更快,因為它們不必呈現。div應用CSS(它使div在應用display: block ),而片段不應用任何默認樣式。⬆回到索引
這是一種組件設計模式,基於創建具有一個單個目標的父組件,為孩子提供必要的屬性,以毫無問題。
它允許在構建新組件時具有聲明性的結構,還可以幫助閱讀代碼以簡化和清潔。
該設計的一個例子將是使孩子們的列表:
< List >
< ListItem > Cat </ ListItem >
< ListItem > Dog </ ListItem >
</ List > const List = ( { children , ... props } ) => (
< ul { ... props } >
{ children }
</ ul >
) ;
const ListItem = ( { children , ... props } ) => {
return (
< li { ... props } >
{ children }
</ li >
) ;
} ;
export { List , ListItem } ;這是一個簡單的例子,但是組件可以隨心所欲,父親和孩子都可以擁有自己的狀態。
興趣鏈接:
在Platzi的博客上,Dezkareid的複合模式將您的反應帶入一個新的水平
珍娜·史密斯(Jenna Smith)用英語的複合組件
肯特·C·多德斯(Kent C. Dodds )的複合組件課程
⬆回到索引
有幾種方法可以從頭開始初始化React項目。最受歡迎的是:
npm create vite@latest my-app -- --template reactnpx create-react-app my-app當今最受歡迎和推薦的選項是Vite。 NPM趨勢來源。
使用框架,最受歡迎的是:
npx create-next-app@latest my-appnpm init gatsby今天,最受歡迎和推薦的選項是NextJ。 NPM趨勢來源
它們每個都是Web應用程序包。他們負責解決您項目的依賴關係,取消開發環境,該開發環境自動刷新每次更改,並用所有必要的靜態文件和更多內容包裝生產應用程序。
⬆回到索引
React Dom是負責渲染瀏覽器的React組件的書店。請記住,React是一個可以在不同環境(移動設備,桌面應用程序,終端...)中使用的庫。
雖然React庫Dry是組件創建引擎,但掛鉤,道具系統和狀態... React Dom是負責在瀏覽器中專門渲染React組件的書店。
例如, React Native會做同樣的事情,但對於移動設備。
⬆回到索引
要學習和統治反應,您需要了解JavaScript。與其他框架和庫不同,例如基於其自己的DSL (特定於領域的語言)的Angular和Vue ,React使用JavaScript語法的擴展名為JSX 。稍後,我們將詳細看到它,但最終,少量寫JavaScript仍然是句法糖。
在React中,所有內容都是JavaScript。更好,更糟。這本書刪除了以前關於編程語言的知識,但是在開始之前,我們將對您需要知道的一些最重要的特徵進行小評論。
如果您已經主導了JavaScript,則可以跳過本章並繼續這本書,但請記住,您可以隨時查看本章作為參考。
eCmascript模塊是JavaScript必須在不同文件之間導入和導出類的本機形式。今天,尤其是如果我們將應用程序包作為WebPack合作,我們將不斷使用此語法。
一方面,我們可以通過默認導出模塊來創建模塊:
// sayHi.js
// exportamos por defecto el módulo sayHi
export default sayHi ( message ) {
console . log ( message )
}
// index.js
// este módulo lo podremos importar con el nombre que queramos
import sayHi from './sayHi.js'
// al ser el módulo exportado por defecto podríamos usar otro nombre
import miduHi from './sayHi.js'我們還可以製作名為模塊的導出,以便模塊具有分配的名稱,並且要導入它在導出時需要精確使用名稱:
// sayHi.js
// podemos usar exportaciones nombradas para mejorar esto
export const sayHi = ( message ) => console . log ( message )
// y se pueden hacer tantas exportaciones de módulos nombrados como queramos
export const anotherHi = msg => alert ( msg )
// index.js
// ahora para importar estos módulos en otro archivo podríamos hacerlo así
import { sayHi , anotherHi } from './sayHi.js'我們在這裡看到的導入被稱為靜態進口。這意味著將在重要的文件加載時向該模塊充電。
也有動態導入,因此我們可以導入在程序執行時或決定時加載的模塊(例如,響應單擊)。
document . querySelector ( 'button' ) . addEventListener ( 'click' , ( ) => {
// los imports dinámicos devuelven una Promesa
import ( './sayHi.js' ) . then ( module => {
// ahora podemos ejecutar el módulo que hemos cargado
module . default ( 'Hola' )
} )
} )當我們使用諸如WebPack或Vite之類的包裝物時,動態導入也很有用,因為這將創建一些將從一般捆綁包中加載的塊(片段)。目標?提高應用程序性能。
還有更多的語法可以使用模塊,但是知道我們已經看到的東西已經足以遵循這本書。
為什麼重要?
啟動React通過可以導入的模塊為您提供庫的不同部分。此外,我們的組件將使它們在文件中分開,並且每個組件都可以使用enmodules導入。
此外,由於性能優化問題,我們可以通過加載更少的信息來使用該頁面來動態導入組件並改善用戶的體驗。
小房間是執行條件的一種方式,而無需使用if語法。您可以說這是避免編寫太多代碼的一種快捷方式。
if ( number % 2 === 0 ) {
console . log ( 'Es par' )
} else {
console . log ( 'Es impar' )
}
// usando ternaria
number % 2 === 0 ? console . log ( 'Es par' ) : console . log ( 'Es impar' )為什麼重要?
在圖形接口中,根據應用程序的狀態或出現在我們的數據,我們將希望在屏幕上渲染一件事,這是非常正常的。為此,使用座機而不是使用if因為它在JSX中更可讀。
箭頭或箭頭函數在Ecmascript 6標準(或ES2015)中添加到JavaScript中。原則上,在創建函數表達時,似乎只是一種更簡單的替代語法:
const nombreDeLaFuncion = function ( param1 , param2 ) {
// instrucciones de la función
}
const nombreDeLaFuncion = ( param1 , param2 ) => { // con arrow function
// instrucciones de la función
}但是,除了語法的變化外,箭頭函數的其他特徵經常在反應中使用。
// return implícito al escribir una sola línea
const getName = ( ) => 'midudev'
// ahorro de parentésis para función de un parámetro
const duplicateNumber = num => num * 2
// se usan mucho como callback en funciones de arrays
const numbers = [ 2 , 4 , 6 ]
const newNumbers = numbers . map ( n => n / 2 )
console . log ( newNumbers ) // [1, 2, 3]它也有一些關於this價值的變化,但是儘管建議掌握它,但實際上並不需要繼續保證這本書。
為什麼重要?
儘管幾年前有React,但我們主要與課程合作,因為在16.8版中鉤子的爆發不再使用。這使更多功能使用。
此外,功能箭頭還可以輕鬆地看到它們生活在組件中。例如,當渲染元素列表時,您將執行數組的.map方法,並且作為回調,您一定會使用匿名箭頭函數。
在JavaScript中,您可以為函數參數提供默認值,以防任何參數傳遞。
// al parámetro b le damos un valor por defecto de 1
function multiply ( a , b = 1 ) {
return a * b ;
}
// si le pasamos un argumento con valor, se ignora el valor por defecto
console . log ( multiply ( 5 , 2 ) ) // 10
// si no le pasamos un argumento, se usa el valor por defecto
console . log ( multiply ( 5 ) ) // 5
// las funciones flecha también pueden usarlos
const sayHi = ( msg = 'Hola React!' ) => console . log ( msg )
sayHi ( ) // 'Hola React!'為什麼重要?
在React中,有兩個非常重要的概念:組件和鉤子。我們現在不會在其中詳細介紹,但重要的是兩者都是由功能構建的。
默認情況下,在沒有參數的情況下,默認情況下將值添加到這些函數的參數是能夠成功控制反應的關鍵。
例如,組件可能無法接收參數,儘管如此,您肯定會希望它們具有一些默認行為。您可以以這種方式獲得它。
文字或鏈模板將文本鏈帶到一個新的級別,允許嵌入其中的表達式。
const inicio = 'Hola'
const final = 'React'
// usando una concatenación normal sería
const mensaje = inicio + " " + final
// con los template literals podemos evaluar expresiones
const mensaje = ` ${ inicio } ${ final } `如您所見,要使用文字,您需要使用``符號''
此外,它們允許我們使用多個線路的文本鏈。
為什麼重要?
在React中,這可以用於不同的事物。創建文本鏈以在接口中顯示的不僅是正常的……它也可用於動態為您的HTML元素創建類。您會看到字面模板無處不在。
從ecmascript 2015中,您可以啟動縮寫屬性的對象使用的對象。這是,如果您想使用與密鑰具有相同名稱的變量,則可以指示初始化一次:
const name = 'Miguel'
const age = 36
const book = 'React'
// antes haríamos esto
const persona = { name : name , age : age , book : book }
// ahora podemos hacer esto, sin repetir
const persona = { name , age , book }為什麼重要?
在React中,有很多次有對象,我們總是希望編寫盡可能多的行以維護我們的代碼易於維護和理解。
非結構化語法是JavaScript的表達式,它允許在不同變量中提取對象的數組或屬性值。
// antes
const array = [ 1 , 2 , 3 ]
const primerNumero = array [ 0 ]
const segundoNumero = array [ 1 ]
// ahora
const [ primerNumero , segundoNumero ] = array
// antes con objetos
const persona = { name : 'Miguel' , age : 36 , book : 'React' }
const name = persona . name
const age = persona . age
// ahora con objetos
const { age , name } = persona
// también podemos añadir valores por defecto
const { books = 2 } = persona
console . log ( persona . books ) // -> 2
// también funciona en funciones
const getName = ( { name } ) => `El nombre es ${ name } `
getName ( persona )為什麼重要?
在React中,有很多基本代碼假設您知道並主導了該語法。認為對象和佈置是數據類型,非常適合保存數據以在接口中表示。因此,能夠輕鬆對待它們將使您的生活更加輕鬆。
知道如何在JavaScript中操縱安排是基本的,認為它是統治的。每種方法都執行特定的操作並返回不同類型的數據。我們將看到的所有方法都會收到一個回調(函數),該回調將為數組的每個元素執行。
我們將回顧一些最常用的方法:
// tenemos este array con diferentes elementos
const networks = [
{
id : 'youtube' ,
url : 'https://midu.tube' ,
needsUpdate : true
} ,
{
id : 'twitter' ,
url : 'https://twitter.com/midudev' ,
needsUpdate : true
} ,
{
id : 'instagram' ,
url : 'https://instagram.com/midu.dev' ,
needsUpdate : false
}
]
// con .map podemos transformar cada elemento
// y devolver un nuevo array
networks . map ( singleNetwork => singleNetwork . url )
// Resultado:
[
'https://midu.tube' ,
'https://twitter.com/midudev' ,
'https://instagram.com/midu.dev'
]
// con .filter podemos filtrar elementos de un array que no
// pasen una condición determinada por la función que se le pasa.
// Devuelve un nuevo array.
networks . filter ( singleNetwork => singleNetwork . needsUpdate === true )
// Resultado:
[
{ id : 'youtube' , url : 'https://midu.tube' , needsUpdate : true } ,
{ id : 'twitter' , url : 'https://twitter.com/midudev' , needsUpdate : true }
]
// con .find podemos buscar un elemento de un array que
// cumpla la condición definida en el callback
networks . find ( singleNetwork => singleNetwork . id === 'youtube' )
// Resultado:
{ id : 'youtube' , url : 'https://midu.tube' , needsUpdate : true }
// con .some podemos revisar si algún elemento del array cumple una condición
networks . some ( singleNetwork => singleNetwork . id === 'tiktok' ) // false
networks . some ( singleNetwork => singleNetwork . id === 'instagram' ) // true為什麼重要?
在React中,存儲我們必須在UI中表示的數據非常正常。這使我們需要多次對待它們,過濾或從中提取信息。理解,了解和掌握這些方法至關重要,因為它們是最常用的方法。
SPRED語法使我們能夠在預期該信息的另一個地方擴展一個具有峰值或對象。為了能夠使用它,我們需要使用三個懸浮點...
const networks = [ 'Twitter' , 'Twitch' , 'Instagram' ]
const newNetwork = 'Tik Tok'
// creamos un nuevo array expandiendo el array networks y
// colocando al final el elemento newNetwork
// utilizando la sintaxis de spread
const allNetworks = [ ... networks , newNetwork ]
console . log ( allNetworks )
// -> [ 'Twitter', 'Twitch', 'Instagram', 'Tik Tok' ]我們可以使用一個對象實現相同的操作,以便我們可以很容易地在另一個對像中擴展其所有屬性。
const midu = { name : 'Miguel' , twitter : '@midudev' }
const miduWithNewInfo = {
... midu ,
youtube : 'https://youtube.com/midudev' ,
books : [ 'Aprende React' ]
}
console . log ( miduWithNewInfo )
// {
// name: 'Miguel',
// twitter: '@midudev',
// youtube: 'https://youtube.com/midudev',
// books: [ 'Aprende React' ]
// }重要的是要注意,這是副本,是的,但膚淺。如果我們在對象內嵌套對象,則應考慮到可以突變參考的對象。讓我們看一個例子。
const midu = {
name : 'Miguel' ,
twitter : '@midudev' ,
experience : {
years : 18 ,
focus : 'javascript'
}
}
const miduWithNewInfo = {
... midu ,
youtube : 'https://youtube.com/midudev' ,
books : [ 'Aprende React' ]
}
// cambiamos un par de propiedades de la "copia" del objeto
miduWithNewInfo . name = 'Miguel Ángel'
miduWithNewInfo . experience . years = 19
// hacemos un console.log del objeto inicial
console . log ( midu )
// en la consola veremos que el nombre no se ha modificado
// en el objeto original pero los años de experiencia sí
// ya que hemos mutado la referencia original
// {
// name: 'Miguel',
// twitter: '@midudev',
// experience: { years: 19, focus: 'javascript' }
// }為什麼重要?
在React中,必須將新元素添加到數組或創建新對象而不突變它們是很正常的。其餘操作員可以幫助我們得到這一點。如果您不太了解JavaScript中價值和參考的概念,那麼對其進行審查將很方便。
語法...在JavaScript中工作了很長時間。該技術稱為REST參數,允許我們在函數中具有不確定數量的參數,並能夠以後作為數組訪問它們。
function suma ( ... allArguments ) {
return allArguments . reduce ( ( previous , current ) => {
return previous + current
} )
}現在,其餘操作員也可以用來將其餘屬性分組為對像或值得一提的。這對於在新變量中提取對像或峰值的特定元素並創建其餘部分的表面副本可能很有用。
const midu = {
name : 'Miguel' ,
twitter : '@midudev' ,
experience : {
years : 18 ,
focus : 'javascript'
}
}
const { name , ... restOfMidu } = midu
console . log ( restOfMidu )
// -> {
// twitter: '@midudev',
// experience: {
// years: 18,
// focus: 'javascript'
// }
// }它也可以與數組一起使用:
const [ firstNumber , ... restOfNumbers ] = [ 1 , 2 , 3 ]
console . log ( firstNumber ) // -> 1
console . log ( restOfNumbers ) // -> [2, 3]為什麼重要?
這是一種有趣的方法,可以消除(形像地)對象的屬性並創建其餘屬性的表面副本。有時,從某些參數中提取我們想要的信息並將其餘的將我們傳遞到另一個級別的對象可能會很有趣。
可選的鏈操作員?.它使您可以安全地讀取嵌套在對象的不同級別中的屬性的值。
這樣,我們要做的就是使用可選鏈,而不是檢查屬性是否存在以訪問它們。
const author = {
name : 'Miguel' ,
libro : {
name : 'Aprendiendo React'
} ,
writeBook ( ) {
return 'Writing!'
}
} ;
// sin optional chaining
( author === null || author === undefined )
? undefined
: ( author . libro === null || author . libro === undefined )
? undefined
: author . libro . name
// con optional chaining
author ?. libro ?. name為什麼重要?
對像是代表UI許多元素時完美的數據結構。你有文章嗎?文章的所有信息肯定會在對像中表示。
由於您的UI更大,更複雜,因此這些對象將具有更多信息,您需要主導可選鏈?.能夠保證訪問您的信息。
⬆回到索引
個性化的鉤子是一個從單詞use開始的函數,可以使用其他鉤子。它們是在不同組件中重用邏輯的理想選擇。例如,我們可以創建一個個性化的鉤子來從會計師那裡提取國家管理:
// ./hooks/useCounter.js
export function useCounter ( ) {
const [ count , setCount ] = useState ( 0 )
const increment = ( ) => setCount ( count + 1 )
const decrement = ( ) => setCount ( count - 1 )
return { count , increment , decrement }
}在組件中使用它:
import { useCounter } from './hooks/useCounter.js'
function Counter ( ) {
const { count , increment , decrement } = useCounter ( )
return (
< >
< button onClick = { decrement } > - </ button >
< span > { count } </ span >
< button onClick = { increment } > + </ button >
</ >
)
}⬆回到索引
useEffect ?儘管通常這些React組件只有一個useEffect但事實是我們可以在組件中擁有盡可能多的useEffect 。當組件的渲染或效果變化時,每個人都將執行。
⬆回到索引
當組件使用Hook useEffect拆卸時,我們可以執行代碼,並以我們要執行的代碼返回函數。在這種情況下,將傳遞的功能作為第一個useEffect參數在安裝組件時將執行,並且在拆卸時返回的函數將執行。
import { useEffect } from 'react'
function Component ( ) {
useEffect ( ( ) => {
console . log ( 'El componente se ha montado' )
return ( ) => {
console . log ( 'El componente se ha desmontado' )
}
} , [ ] )
return < h1 > Ejemplo </ h1 >
}這對於清潔組件中創建的資源(例如瀏覽器事件或取消對API的請求)非常有用。
⬆回到索引
useEffect中API的請求當我們向API提出請求時,我們可以將其取消以防止在使用AbortController拆卸組件時,如我們在此示例中所做的那樣:
useEffect ( ( ) => {
// Creamos el controlador para abortar la petición
const controller = new AbortController ( )
// Recuperamos la señal del controlador
const { signal } = controller
// Hacemos la petición a la API y le pasamos como options la señal
fetch ( 'https://jsonplaceholder.typicode.com/posts/1' , { signal } )
. then ( res => res . json ( ) )
. then ( json => setMessage ( json . title ) )
. catch ( error => {
// Si hemos cancelado la petición, la promesa se rechaza
// con un error de tipo AbortError
if ( error . name !== 'AbortError' ) {
console . error ( error . message )
}
} )
// Si se desmonta el componente, abortamos la petición
return ( ) => controller . abort ( )
} , [ ] )這也適用於axios :
useEffect ( ( ) => {
// Creamos el controlador para abortar la petición
const controller = new AbortController ( )
// Recuperamos la señal del controlador
const { signal } = controller
// Hacemos la petición a la API y le pasamos como options la señal
axios
. get ( 'https://jsonplaceholder.typicode.com/posts/1' , { signal } )
. then ( res => setMessage ( res . data . title ) )
. catch ( error => {
// Si hemos cancelado la petición, la promesa se rechaza
// con un error de tipo AbortError
if ( error . name !== 'AbortError' ) {
console . error ( error . message )
}
} )
// Si se desmonta el componente, abortamos la petición
return ( ) => controller . abort ( )
} , [ ] )⬆回到索引
React中的鉤子有兩個基本規則:
⬆回到索引
useEffect和useLayoutEffect有什麼區別?儘管兩者都非常相似,但在運行時它們的差異很小。
React在渲染後完全更新DOM後, useLayoutEffect將立即同步執行。如果您需要恢復DOM的元素並訪問其尺寸或在屏幕上的位置,則可能會很有用。
渲染後, useEffect是異步執行的,但不能確保DOM已更新。也就是說,如果您需要恢復DOM的元素並訪問其尺寸或在屏幕上的位置,則您將無法使用useEffect進行操作,因為您不能保證DOM已更新。
通常,有99%的時間,您將需要使用useEffect ,此外,它具有更好的性能,因為它不會阻止渲染。
⬆回到索引
由於包括16.8.0鉤子,因此功能組件幾乎可以完成分類組件的所有操作。
儘管對這個問題沒有明確的答案,但通常這些功能組件更易於讀寫和寫入,並且總體上可以更好地表現。
另外,鉤子只能在功能組件中使用。這很重要,因為通過創建自定義掛鉤,我們可以重複使用邏輯,並可以簡化我們的組件。
另一方面,類組件使我們能夠使用組件的生命週期,這是我們只能使用使用useEffect功能組件無法做到的。
參考:
⬆回到索引
純組件是那些沒有條件且沒有副作用的組件。這意味著他們沒有邏輯不呈現接口。
它們更容易測試和維護。此外,它們更容易理解,因為它們沒有復雜的邏輯。
為了在React中創建一個純組件,我們使用一個函數:
function Button ( { text } ) {
return (
< button >
{ text }
</ button >
)
}在這種情況下, Button組件會收到自己的字符串text 。 Button組件在text中收到的文本呈現一個按鈕。
⬆回到索引
當我們在服務器上渲染應用程序時,React會生成靜態HTML。此靜態HTML只是一個字符串,其中包含將在頁面上顯示的HTML。
當瀏覽器接收靜態HTML時,它會在頁面上呈現。但是,該靜態HTML沒有交互性。它沒有事件,沒有邏輯,沒有條件等。我們可以說它沒有生命。
為了使此靜態HTML具有互動性,React需要靜態HTML成為反應組分樹。這稱為水合。
通過這種方式,在客戶端中,反應重複使用此靜態HTML,並致力於將事件附加到元素上,執行我們對組件的影響並調和組件狀態。
⬆回到索引
服務器端渲染是一種技術,它包括在服務器上渲染HTML並將其發送給客戶端。這使我們可以在加載JavaScript之前查看應用程序接口。
這種技術使我們能夠改善用戶體驗並改善應用程序的SEO。
⬆回到索引
要使用從頭開始的React創建側面渲染服務器,我們可以使用react-dom/server軟件包,該軟件包允許我們在服務器上呈現React的組件。
讓我們看一個示例,說明如何使用scratch with express創建側面渲染服務器:
import express from 'express'
import React from 'react'
import { renderToString } from 'react-dom/server'
const app = express ( )
app . get ( '/' , ( req , res ) => {
const html = renderToString ( < h1 > Hola mundo </ h1 > )
res . send ( html )
} )訪問路線/時,這將返回應用程序的HTML。
< h1 data-reactroot ="" > Hola mundo </ h1 >⬆回到索引
像JavaScript功能一樣,反應組件也可以產生效果(附帶效果)。附帶效應意味著組件操縱或讀取不在其範圍內的信息。
在這裡,您可以看到一個具有附帶效果的組件的簡單示例。讀取和修改組件之外的變量的組件。這使得無法知道每次使用的組件都會呈現什麼,因為我們不知道count的值:
let count = 0
function Counter ( ) {
count = count + 1
return (
< p > Contador: { count } </ p >
)
}
export default function Counters ( ) {
return (
< >
< Counter />
< Counter />
< Counter />
</ >
)⬆回到索引
使用React表單時,我們有兩種類型的組件:受控組件和非控制組件。
受控組件:具有控制組件值的狀態的組件。因此,當狀態更改時,將更新組件的值。
這種類型的組件的優點是它們更易於測試,因為它們不取決於接口。它們還使我們能夠非常輕鬆地創建驗證。缺點是它們更加複雜,可以創建和維護。此外,它們的性能可能會較差,因為每次輸入值更改時會導致重新表面。
非控制的組件是沒有控制組件值的狀態的組件。組件的狀態由內部瀏覽器控制。要了解組件的值,我們必須讀取DOM值。
這種類型的組件的優點是它們非常容易創建,而您不必保持狀態。此外,性能更好,因為在更改輸入的值時不必重新代表。不好的事情是,您必須直接處理DOM並創建命令式代碼。
// Controlado:
const [ value , setValue ] = useState ( '' )
const handleChange = ( ) => setValue ( event . target . value )
< input type = "text" value = { value } onChange = { handleChange } / >
// No controlado:
< input type = "text" defaultValue = "foo" ref = { inputRef } />
// Usamos `inputRef.current.value` para leer el valor del input⬆回到索引
高階組件是將組件作為參數接收並返回組件的函數。
function withLayout ( Component ) {
return function ( props ) {
return < main >
< section >
< Component { ... props } />
</ section >
</ main >
}
}在這種情況下, withLayout函數將一個組件作為參數接收並返回組件。返回的組件渲染以佈局中的參數傳遞的組件。
這是一種使我們能夠重複使用代碼的模式,因此我們可以以簡單的方式向組件注入功能,樣式或其他任何內容。
隨著鉤子的到來,HOCS變得越來越流行,但在某些情況下仍然使用它們。
⬆回到索引
它們是一種反應設計模式,使我們能夠在組件之間重複使用代碼,並將信息注入組件的渲染中。
< DataProvider render = { data => (
< h1 > Hello { data . target } </ h1 >
) } />在這種情況下, DataProvider組件會接收render函數。在那裡,我們指出您應該使用收到的信息作為參數呈現的內容。
具有功能的DataProvider實現可能是以下內容:
function DataProvider ( { render } ) {
const data = { target : 'world' }
return render ( data )
}您還可以在組件中使用children自己找到這種模式。
< DataProvider >
{ data => (
< h1 > Hello { data . target } </ h1 >
) }
</ DataProvider >實施將相似:
function DataProvider ( { children } ) {
const data = { target : 'world' }
return children ( data )
}該模式由大型文庫(例如react-router , formik或react-motion使用。
⬆回到索引
if ?在React中,我們不能在組件的渲染中使用if ,因為它不是JavaScript的有效表達,而是一個陳述。表達式是返回值,語句不返回任何值的表達式。
在JSX中,我們只能使用表達式,這就是為什麼我們使用土地,這就是表達式。
// Esto no funciona
function Button ( { text } ) {
return (
< button >
{ if ( text ) { return text } else { return 'Click' } }
</ button >
)
}
// ✅ Esto funciona
function Button ( { text } ) {
return (
< button >
{ text ? text : 'Click' }
</ button >
)
}以同樣的方式,我們不能while組件的渲染中for或switch 。
⬆回到索引
更新React的狀態時,我們必須使用Hook useState促進的功能來更新狀態。
const [ count , setCount ] = useState ( 0 )
setCount ( count + 1 )為什麼需要這?首先,反應狀態必須不變。也就是說,我們不能直接修改狀態,但是我們必須始終為新狀態創建一個新值。
這樣一來,UI的完整性涉及其呈現的數據始終是正確的。
另一方面,調用功能允許React知道狀態已更改,並且必須在必要時重新代表該組件。此外,這是不同步的,因此我們可以根據需要和反應的多次調用setCount ,將負責在認為適當的情況下更新狀態。
⬆回到索引
在類成分中,組件的生命週期分為三個階段:
在這個生命週期中,組件中有一組方法。
這些方法在類中定義,並按照下面顯示的順序執行:
在每種方法中,我們都可以執行代碼,使我們能夠控制組件的行為。
⬆回到索引
index用作React列表中的密鑰是不好的做法?當我們渲染元素列表時,React需要知道哪些元素已更改,它們已被添加或消除。
為此,React需要為列表中的每個元素一個唯一的密鑰。如果我們不傳遞鑰匙,則React將元素索引用作密鑰。
const List = ( ) => {
const [ items , setItems ] = useState ( [ 'Item 1' , 'Item 2' , 'Item 3' ] )
return (
< ul >
{ items . map ( ( item , index ) => (
< li key = { index } > { item } </ li >
) ) }
</ ul >
)
}在這種情況下,React將元素索引用作key 。如果列表重新排序或數組的元素被取消,則可能是一個問題,因為元素的索引發生了變化。
在這種情況下,React不知道哪些元素已更改,並且可能發生錯誤。
看到問題的一個示例:
const List = ( ) => {
const [ items , setItems ] = useState ( [ 'Item 1' , 'Item 2' , 'Item 3' ] )
const handleRemove = ( index ) => {
const newItems = [ ... items ]
newItems . splice ( index , 1 )
setItems ( newItems )
}
return (
< ul >
{ items . map ( ( item , index ) => (
< li key = { index } >
{ item }
< button onClick = { ( ) => handleRemove ( index ) } > Eliminar </ button >
</ li >
) ) }
</ ul >
)
}⬆回到索引
useMemo用途是什麼?掛鉤useMemo是一個鉤子,使我們能夠記住功能的結果。這意味著,如果我們傳遞的函數作為參數尚未更改,則不會再次執行,並且返回已經計算的結果。
import { useMemo } from 'react'
function Counter ( { count } ) {
const double = useMemo ( ( ) => count * 2 , [ count ] )
return (
< div >
< p > Contador: { count } </ p >
< p > Doble: { double } </ p >
</ div >
)
}在這種情況下, Counter組件會收到一個數字的count 。該組件計算兩倍,並在屏幕上顯示。
掛鉤useMemo接收兩個參數:一個函數和一系列依賴項。當組件是第一次渲染時,並且當一個單元之一更改時,在本示例中,該功能將count 。
優勢是,如果count不變,則避免了雙重計算,並且返回已經計算的值。
⬆回到索引
useMemo來優化我們的組件是一個好主意嗎?否useMemo是一種允許我們優化組件的工具,但它並不是一個使我們的組件更快的神奇工具。有時,一個值的計算是如此之快,以至於不值得記住它。即使在某些情況下,它也可以使記憶更慢,而不是再次計算它。
⬆回到索引
useCallback用途是什麼? Hook useCallback是一個掛鉤,它使我們能夠記住功能。這意味著,如果我們通過參數傳遞的函數沒有更改,則不會再次執行,並且返回已經計算的功能。
import { useCallback } from 'react'
function Counter ( { count , onIncrement } ) {
const handleIncrement = useCallback ( ( ) => {
onIncrement ( count )
} , [ count , onIncrement ] )
return (
< div >
< p > Contador: { count } </ p >
< button onClick = { handleIncrement } > Incrementar </ button >
</ div >
)
}在這種情況下, Counter組件會收到自己count數字和一個自己的onIncrement ,該功能是按下按鈕時執行的函數。
Hook useCallback接收兩個參數:一個函數和一系列依賴項。當組件首次渲染時,並且當任何依賴count更改onIncrement ,在此示例中,該函數將執行。
優點是,如果count或onIncrement本身不變,則避免了新功能的創建,並且返回已經計算的函數。
⬆回到索引
useCallback優化我們的組件是一個好主意嗎?否useCallback是一種使我們能夠優化組件的工具,但它並不是一個使我們的組件更快的神奇工具。有時,函數的創建是如此之快,以至於不值得記住它。即使在某些情況下,它也可以使記憶更慢,而不是再次創建它。
⬆回到索引
useCallback和useMemo有什麼區別? useCallback和useMemo之間的區別在於, useCallback記住了一個函數並useMemo函數的結果。
無論如何,實際上, useCallback是useMemo的專業版本。實際上,您可以使用useMemo模擬useCallback的功能:
const memoizedCallback = useMemo ( ( ) => {
return ( ) => {
doSomething ( a , b )
}
} , [ a , b ] )⬆回到索引
裁判允許我們創建對DOM OA元素的引用,該值將在渲染之間維護。可以通過createRef命令或使用Hook useRef聲明它們。
⬆回到索引
useRef如何工作?在下面的示例中,我們將將DOM中的參考保存到A <input>元素,當我們單擊按鈕時,我們將將焦點更改為該元素。
import { useRef } from 'react'
function TextInputWithFocusButton ( ) {
const inputEl = useRef ( null )
const onButtonClick = ( ) => {
// `current` apunta al elemento inputEl montado
inputEl . current . focus ( )
}
return (
< >
< input ref = { inputEl } type = "text" />
< button onClick = { onButtonClick } > Focus the input </ button >
</ >
)
}我們使用useRef創建一個inputEl參考,並將其作為其自己的ref將其傳遞到<input>元素。安裝組件時, inputEl引用指向DOM的<input>元素。
要訪問DOM的元素,我們使用參考current 。
⬆回到索引
useLayoutEffect做什麼? useLayoutEffect工作原理就像鉤useEffect一樣,除了閱讀所有DOM突變後,它會同步觸發。
Llama useLayoutEffect在組件的最高級別。
import { useLayoutEffect } from 'react' ;
useLayoutEffect ( ( ) => {
return ( ) => {
}
} , [ ] ) ; useLayoutEffect收到兩個論點:
儘管useEffect是最常用的渲染鉤,但如果DOM靜音的效果會改變效果和渲染之間的外觀,那麼您可以很方便地使用useLayoutEffect 。
useLayoutEffect執行順序useLayoutEffect執行順序,因為它是同步執行的,當時React完成了所有突變的執行,但是在將其呈現在屏幕上之前,如下:如下:
⬆回到索引
無狀態組件是沒有條件的組件。這些組件是通過function創建的,無法訪問應用程序的狀態。這些組件的優點在於,創建純組件(始終為同一道具渲染)更容易。
// Este es un ejemplo de componente stateless
function Button ( { text } ) {
return (
< button >
{ text }
</ button >
)
}⬆回到索引
為了防止事件在React中的默認行為,我們必須使用preventDefault方法:
function Form ( { onSubmit } ) {
const handleSubmit = ( event ) => {
event . preventDefault ( )
onSubmit ( )
}
return < form onSubmit = { handleSubmit } >
< input type = "text" />
< button type = "submit" > Enviar </ button >
</ form >
}⬆回到索引
StrictMode是什麼? StrictMode是一個允許我們激活React中一些開發檢查的組件。例如,它檢測到使用的不必要或過時功能的組件。
import { StrictMode } from 'react'
function App ( ) {
return (
< StrictMode >
< Component />
</ StrictMode >
)
}⬆回到索引
React組件可以通過兩種方式出口:
要導出默認組件,我們使用“保留”一詞default :
// button.jsx
export default function Button ( ) {
return < button > Click </ button >
}
// App.jsx
import Button from './button.jsx'
function App ( ) {
return < Button />
}默認導出的最大缺點是導入時,您可以使用所需的名稱。這會帶來問題,因為您可能並不總是在項目中使用相同的方法或使用重要的名稱。
// button.jsx
export default function Button ( ) {
return < button > Click </ button >
}
// App.jsx
import MyButton from './button.jsx'
function App ( ) {
return < MyButton />
}
// Otro.jsx
import Button from './button.jsx'
function Otro ( ) {
return < Button />
}指定的出口迫使我們在所有文件中使用相同的名稱,因此,我們確保我們始終使用正確的名稱。
// button.jsx
export function Button ( ) {
return < button > Click </ button >
}
// App.jsx
import { Button } from './button.jsx'
function App ( ) {
return < Button />
}⬆回到索引
要導出同一文件的多個組件,我們可以使用名為:
// button.jsx
export function Button ( { children } ) {
return < button > { children } </ button >
}
export function ButtonSecondary ( { children } ) {
return < button class = "btn-secondary" > { children } </ button >
}⬆回到索引
要動態導入React中的組件,我們必須使用import()函數, lazy()反應方法和Suspense組件。
// App.jsx
import { lazy , Suspense } from 'react'
const Button = lazy ( ( ) => import ( './button.jsx' ) )
export default function App ( ) {
return (
< Suspense fallback = { < div > Cargando botón... </ div > } >
< Button />
</ Suspense >
)
}
// button.jsx
export default function Button ( ) {
return < button > Botón cargado dinámicamente </ button >
}我們將詳細了解我們使用的每個元素:
import()函數是eCmascript標準的一部分,允許我們動態導入模塊。此功能返回通過導入模塊解決的承諾。
React lazy()方法使我們能夠創建一個遞延的組件。此方法接收一個必須返回組件解決的承諾的函數。在這種情況下,它將使用我們在button.jsx文件中的組件解決。請記住,返回lazy()的組件必須是一個反應組件,並且默認情況下導出( export default )。
Suspense組件允許我們在加載組件時顯示一條消息。該組件會收到一個fallback ,這是在加載組件時顯示的消息。
⬆回到索引
在React中,我們的應用程序是由組件創建的。這些組件可以導入靜態或動態。
靜態組件的導入是在React中導入組件的最常見方法。在這種情況下,組件是在文件頂部導入並在代碼中渲染的。問題是,如果我們始終這樣做,我們很可能會攜帶從一開始就不會使用的組件。
import { useState } from 'react'
// importamos de forma estática el componente de la Modal
import { SuperBigModal } from './super-big-modal.jsx'
// mostrar modal si el usuario da click en un botón
export default function App ( ) {
const [ showModal , setShowModal ] = useState ( false )
return (
< div >
< button onClick = { ( ) => setShowModal ( true ) } > Mostrar modal </ button >
{ showModal && < SuperBigModal /> }
</ div >
)
}此SuperBigModal組件是靜態導入的,因此從一開始就加載了。但是,如果用戶不單擊按鈕顯示模式,會發生什麼?在這種情況下,該組件攜帶它不使用它。
如果我們想為用戶提供最佳體驗,則必須嘗試盡快攜帶該應用程序。因此,建議動態導入從一開始就不會使用的組件。
import { useState , lazy , Suspense } from 'react'
// importamos de forma dinámica el componente de la Modal
const SuperBigModal = lazy ( ( ) => import ( './super-big-modal.jsx' ) )
// mostrar modal si el usuario da click en un botón
export default function App ( ) {
const [ showModal , setShowModal ] = useState ( false )
return (
< div >
< button onClick = { ( ) => setShowModal ( true ) } > Mostrar modal </ button >
< Suspense fallback = { < div > Cargando modal... </ div > } >
{ showModal && < SuperBigModal /> }
</ Suspense >
</ div >
)
}通過這種方式,導入SuperBigModal組件的代碼部分會動態充電,也就是說,當用戶單擊按鈕以顯示模式時。
根據您使用的Web應用程序包裝計算機及其配置,負載的結果可能是不同的(有些人會創建一個文件,而不是主捆綁包,而另一些則可以進行HTML的流式,但代碼的意圖是相同的。
因此,當組件從一開始就不使用時,我們必須始終嘗試動態加載它們,尤其是當它們是用戶互動的背後時。我們的應用程序的完整路線也可能發生同樣的情況。如果用戶訪問主頁,為什麼還要加載有關網站的。
⬆回到索引
不,默認情況下沒有必要導出組件能夠動態加載它們。我們可以以指定的方式導出它們並動態加載它們……但是這不是最建議的,因為必要的代碼更大得多。
// button.jsx
// exportamos el componente Button de forma nombrada
export function Button ( ) {
return < button > Botón cargado dinámicamente </ button >
}
// app.jsx
import { lazy , Suspense } from 'react'
// Al hacer el import dinámico, debemos especificar el nombre del componente que queremos importar
// y hacer que devuelva un objeto donde la key default pasar a ser el componente nombrado
const Button = lazy (
( ) => import ( './button.jsx' )
. then ( ( { Button } ) => ( { default : Button } ) )
)
export default function App ( ) {
return (
< div >
< Suspense fallback = { < div > Cargando botón... </ div > } >
< Button />
</ Suspense >
</ div >
)
}另一個選項是具有默認情況下導出組件的中間文件,這是我們動態導入的中間文件。
// button-component.jsx
// exportamos el componente Button de forma nombrada
export function Button ( ) {
return < button > Botón cargado dinámicamente </ button >
}
// button.jsx
export { Button as default } from './button-component.jsx'
// app.jsx
import { lazy , Suspense } from 'react'
const Button = lazy ( ( ) => import ( './button.jsx' ) )
export default function App ( ) {
return (
< div >
< Suspense fallback = { < div > Cargando botón... </ div > } >
< Button />
</ Suspense >
</ div >
)
}⬆回到索引
上下文是通過組件的層次結構傳遞數據的一種方式,而無需在每個級別上手動通過。
為了在React中創建上下文,我們使用Hook createContext :
import { createContext } from 'react'
const ThemeContext = createContext ( )要使用上下文,我們必須用Provider組件包裹組件樹:
< ThemeContext . Provider value = "dark" >
< App />
</ ThemeContext . Provider >要消耗上下文,我們必須使用鉤子useContext :
import { useContext } from 'react'
function Button ( ) {
const theme = useContext ( ThemeContext )
return < button className = { theme } > Haz clic aquí </ button >
}⬆回到索引
SyntheticEvent是什麼? SyntheticEvent是本機瀏覽器事件的抽象。這允許React在所有瀏覽器中具有一致的行為。
在SyntheticEvent中,可以找到對nativeEvent屬性中對本機事件的引用
function App ( ) {
function handleClick ( event ) {
console . log ( event )
}
return < button onClick = { handleClick } > Haz clic aquí </ button >
}⬆回到索引
flushSync是什麼? flushSync(callback)力在比例回調內同步運行所有狀態更新。因此,可確保立即更新DOM。
import { flushSync } from "react-dom"
function App ( ) {
const handleClick = ( ) => {
setId ( 1 )
// component no hace re-render
flushSync ( ( ) => {
setId ( 2 )
// component re-renderiza aquí
} )
// component ha sido re-renderizado y el DOM ha sido actualizado ✅
flushSync ( ( ) => {
setName ( "John" )
// component no hace re-render
setEmail ( "[email protected]" )
// component re-renderiza aquí
} )
// component ha sido re-renderizado y el DOM ha sido actualizado ✅
}
return < button onClick = { handleClick } > Haz clic aquí </ button >
}注意: flushSync可以顯著影響性能。適度使用它。
⬆回到索引
邊界是使我們能夠處理組件樹中發生的錯誤的組件。要創建邊界錯誤,我們必須創建一個實現componentDidCatch方法的組件:
class ErrorBoundary extends React . Component {
constructor ( props ) {
super ( props )
this . state = { hasError : false }
}
static getDerivedStateFromError ( error ) {
return { hasError : true }
}
componentDidCatch ( error , errorInfo ) {
console . log ( error , errorInfo )
}
render ( ) {
if ( this . state . hasError ) {
return < h1 > Algo ha ido mal </ h1 >
}
return this . props . children
}
}通過這種方式,我們可以捕獲組件樹中發生的錯誤,並顯示自定義錯誤消息,同時防止我們的應用程序完全破裂。
現在,我們可以將組件樹包裹在ErrorBoundary組件中:
< ErrorBoundary >
< App />
</ ErrorBoundary >我們可以在組件樹的任何級別上創建邊界誤差,這樣我們就可以對錯誤進行更精細的控制。
< ErrorBoundary >
< App />
< ErrorBoundary >
< SpecificComponent />
</ ErrorBoundary >
</ ErrorBoundary >目前,沒有本機在React函數中創建邊界錯誤。要在功能中創建邊界錯誤,您可以使用React-Ra-Boundary書店。
⬆回到索引
參考遠期或前向RE REFS是一種使我們可以從父親組成部分訪問子女組成部分的參考。
// Button.jsx
import { forwardRef } from 'react'
export const Button = forwardRef ( ( props , ref ) => (
< button ref = { ref } className = "rounded border border-sky-500 bg-white" >
{ props . children }
</ button >
) ) ;
// Parent.jsx
import { Button } from './Button'
import { useRef } from 'react'
const Parent = ( ) => {
const ref = useRef ( )
useEffect ( ( ) => {
// Desde el padre podemos hacer focus
// al botón que tenemos en el hijo
ref . current ?. focus ?. ( )
} , [ ref . current ] )
return (
< Button ref = { ref } > My button </ Button >
)
}在此示例中,我們恢復了按鈕(HTML元素<button> )的引用,並恢復父親組件( Parent ),以便能夠重點放在子組件( Button )中的forwardRef 。
對於絕大多數組件而言,這不是必需的,但對於設計系統或可重複使用的第三方可能很有用。
⬆回到索引
React提供了一種在執行時間和開發模式下驗證組件的道具類型的方法。這對於確保正確使用組件很有用。
該軟件包稱為prop-types ,可以使用npm install prop-types裝。
import PropTypes from "prop-types"
function App ( props ) {
return < h1 > { props . title } </ h1 >
}
App . propTypes = {
title : PropTypes . string . isRequired ,
}在此示例中,我們驗證了Pro title是string ,這是強制性的。
已經定義了一系列的預言,可以幫助您檢查最常見的道具的類型:
PropTypes . number // número
PropTypes . string // string
PropTypes . array // array
PropTypes . object // objeto
PropTypes . bool // un booleano
PropTypes . func // función
PropTypes . node // cualquier cosa renderizable en React, como un número, string, elemento, array, etc.
PropTypes . element // un elemento React
PropTypes . symbol // un Symbol de JavaScript
PropTypes . any // cualquier tipo de dato可以向所有這些添加isRequired屬性,以表明它是強制性的。
另一個選項是使用TypeScript,一種編譯JavaScript並提供靜態類型驗證的編程語言。請記住,雖然Typescript在編譯時間中檢查類型時,這些主題會在執行時間內進行。
⬆回到索引
為了驗證作為Prop傳遞的對象的屬性,我們可以使用shape PropTypes :
import PropTypes from "prop-types"
function App ( { title } ) {
const { text , color } = title
return < h1 style = { { color } } > { text } </ h1 >
}
App . propTypes = {
title : PropTypes . shape ( {
text : PropTypes . string . isRequired ,
color : PropTypes . string . isRequired ,
} ) ,
}⬆回到索引
為了驗證作為Prop傳遞的數組的屬性,我們可以使用PropTypes的arrayOf屬性:
import PropTypes from "prop-types"
function App ( { items } ) {
return (
< ul >
{ items . map ( ( item ) => (
< li key = { item . text } > { item . text } </ li >
) ) }
</ ul >
)
}
App . propTypes = {
items : PropTypes . arrayOf (
PropTypes . shape ( {
text : PropTypes . string . isRequired ,
} )
) . isRequired ,
}在這種情況下,我們正在驗證items是一個數組,其每個元素都是帶有string類型text的對象。另外,本身是強制性的。
⬆回到索引
創建React的原因之一是避免XSS(跨站點腳本)攻擊,防止用戶可以將HTML代碼注入頁面。
因此,在嘗試評估包含HTML的字符串時會自動進行反應。例如,如果我們嘗試渲染以下字符串:
const html = "<h1>My title</h1>"
function App ( ) {
return < div > { html } </ div >
}我們會看到,它沒有呈現HTML,而是逃脫了:
< div > <h1>My title</h1> </ div >Sin embargo, hay ocasiones en las que es necesario inyectar HTML directamente en un componente. Ya sea por traducciones que tenemos, porque viene el HTML desde el servidor y ya viene saneado, o por un componente de terceros.
Para ello, podemos usar la propiedad dangerouslySetInnerHTML :
const html = "<h1>My title</h1>"
function App ( ) {
return < div dangerouslySetInnerHTML = { { __html : html } } />
}Ahora sí veremos el HTML renderizado:
< div > < h1 > My title </ h1 > </ div >Como ves, el nombre ya nos indica que es una propiedad peligrosa y que debemos usarla con cuidado. Intenta evitarla siempre que puedas y sólo recurre a ella cuando realmente no tengas otra opción.
⬆回到索引
Digamos que tenemos un componente App que recibe un objeto props con todas las props que necesita:
function App ( props ) {
return < h1 > { props . title } </ h1 >
} Y que tenemos otro componente Layout que recibe un objeto props con todas las props que necesita:
function Layout ( props ) {
return (
< div >
< App { ... props } />
</ div >
)
} En este caso, Layout está pasando todas las props que recibe a App . Esto puede ser una mala idea por varias razones:
Layout recibe una prop que no necesita, la pasará a App y éste puede que no la use. Esto puede ser confuso para el que lea el código.⬆回到索引
El propósito del atributo "key" en React es proporcionar una identificación única a cada elemento en una lista renderizada dinámicamente. Esto permite a React identificar qué elementos han cambiado, añadido o eliminado de la lista cuando se realiza una actualización.
Cuando se renderiza una lista en React sin el atributo "key", React puede tener dificultades para identificar correctamente los cambios en la lista, lo que puede resultar en un comportamiento inesperado, como la re-renderización innecesaria de elementos o la pérdida de estado de los componentes.
Por lo tanto, es importante utilizar el atributo "key" de manera correcta y única para cada elemento de la lista, preferiblemente utilizando identificadores únicos de cada elemento en lugar de índices de array, para garantizar un rendimiento óptimo y un comportamiento predecible en la aplicación.
Ejemplo de cómo utilizar el atributo "key" en React:
import React from 'react' ;
const ListaItems = ( { items } ) => {
return (
< ul >
{ items . map ( ( item ) => (
< li key = { item . id } > { item . nombre } </ li >
) ) }
</ ul >
) ;
} ;
export default ListaItems ;⬆回到索引
Existe una fina línea hoy en día entre qué es una biblioteca o un framework. Oficialmente, React se autodenomina como biblioteca. Esto es porque para poder crear una aplicación completa, necesitas usar otras bibliotecas.
Por ejemplo, React no ofrece un sistema de enrutado de aplicaciones oficial. Por ello, hay que usar una biblioteca como React Router o usar un framework como Next.js que ya incluye un sistema de enrutado.
Tampoco puedes usar React para añadir las cabeceras que van en el <head> en tu aplicación, y también necesitarás otra biblioteca o framework para solucionar esto.
Otra diferencia es que React no está opinionado sobre qué empaquetador de aplicaciones usar. En cambio Angular en su propio tutorial ya te indica que debes usar @angular/cli para crear una aplicación, en cambio React siempre te deja la libertad de elegir qué empaquetador usar y ofrece diferentes opciones.
Aún así, existe gente que considera a React como un framework. Aunque no hay una definición oficial de qué es un framework, la mayoría de la gente considera que un framework es una biblioteca que incluye otras bibliotecas para crear una aplicación completa de forma opinionada y casi sin configuración.
Por ejemplo, Next.js se podría considerar un framework de React porque incluye React, un sistema de enrutado, un sistema de renderizado del lado del servidor, etc.
⬆回到索引
useImperativeHandle胡克手有什麼用?Nos permite definir qué propiedades y métodos queremos que sean accesibles desde el componente padre.
En el siguiente ejemplo vamos a crear un componente TextInput que tiene un método focus que cambia el foco al elemento <input> .
import { useRef , useImperativeHandle } from 'react'
function TextInput ( props , ref ) {
const inputEl = useRef ( null )
useImperativeHandle ( ref , ( ) => ( {
focus : ( ) => {
inputEl . current . focus ( )
}
} ) )
return (
< input ref = { inputEl } type = "text" />
)
} Creamos una referencia inputEl con useRef y la pasamos al elemento <input> como prop ref . Cuando el componente se monta, la referencia inputEl apunta al elemento <input> del DOM.
Para acceder al elemento del DOM, usamos la propiedad current de la referencia.
Para que el componente padre pueda acceder al método focus , usamos el hook useImperativeHandle . Este hook recibe dos parámetros: una referencia y una función que devuelve un objeto con las propiedades y métodos que queremos que sean accesibles desde el componente padre.
⬆回到索引
cloneElement方法是什麼?Te permite clonar un elemento React y añadirle o modificar las props que recibe.
import { cloneElement } from 'react'
const Hello = ( { name } ) => < h1 > Hello { name } </ h1 >
const App = ( ) => {
const element = < Hello name = "midudev" />
return (
< div >
{ cloneElement ( element , { name : 'TMChein' } ) }
{ cloneElement ( element , { name : 'Madeval' } ) }
{ cloneElement ( element , { name : 'Gorusuke' } ) }
</ div >
)
} En este ejemplo, clonamos element que tenía la prop midudev y le pasamos una prop name diferente cada vez. Esto renderizará tres veces el componente Hello con los nombres TMChein , Madeval y Gorusuke . Sin rastro de la prop original.
Puede ser útil para modificar un elemento que ya nos viene de un componente padre y del que no tenemos posibilidad de re-crear con el componente.
⬆回到索引
Los portales nos permiten renderizar un componente en un nodo del DOM que no es hijo del componente que lo renderiza.
Es perfecto para ciertos casos de uso como, por ejemplo, modales:
import { createPortal } from 'react-dom'
function Modal ( ) {
return createPortal (
< div className = "modal" >
< h1 > Modal </ h1 >
</ div > ,
document . getElementById ( 'modal' )
)
} createPortal acepta dos parámetros:
En este caso el modal se renderiza en el nodo #modal del DOM.
⬆回到索引
StrictMode渲染應用程序兩倍? Cuando el modo StrictMode está activado, React monta los componentes dos veces (el estado y el DOM se preserva). Esto ayuda a encontrar efectos que necesitan una limpieza o expone problemas con race conditions .
⬆回到索引
Como developers, nuestra misión es encontrar el equilibrio entre rendimiento y experiencia, intentando priorizar siempre cómo el usuario sentirá la aplicación. No hay ningún caso lo suficientemente justificado para renderizar en pantalla miles de datos.
El espacio de visualización es limitado ( viewport ), al igual que deberían serlo los datos que añadimos al DOM.
⬆回到索引
useEffect中止提取請願書? Si quieres evitar que exista una race condition entre una petición asíncrona y que el componente se desmonte, puedes usar la API de AbortController para abortar la petición cuando lo necesites:
import { useEffect , useState } from 'react'
function Movies ( ) {
const [ movies , setMovies ] = useState ( [ ] )
useEffect ( ( ) => {
// creamos un controlador para abortar la petición
const abortController = new AbortController ( )
// pasamos el signal al fetch para que sepa que debe abortar
fetchMovies ( { signal : abortController . signal } )
. then ( ( ) => {
setMovies ( data . results )
} ) . catch ( error => {
if ( error . name === 'AbortError' ) {
console . log ( 'fetch aborted' )
}
} )
return ( ) => {
// al desmontar el componente, abortamos la petición
// sólo funcionará si la petición sigue en curso
abortController . abort ( )
}
} )
// ...
}
// Debemos pasarle el parámetro signal al `fetch`
// para que enlace la petición con el controlador
const fetchMovies = ( { signal } ) => {
return fetch ( 'https://api.themoviedb.org/3/movie/popular' , {
signal // <--- pasamos el signal
} ) . then ( response => response . json ( ) )
}De esta forma evitamos que se produzca un error por parte de React de intentar actualizar el estado de un componente que ya no existe, además de evitar que se produzcan llamadas innecesarias al servidor.
⬆回到索引
En lugar de recibir la lista en una sola llamada a la API (lo cual sería negativo tanto para el rendimiento como para el propio servidor y tiempo de respuesta de la API), podríamos implementar un sistema de paginación en el cual la API recibirá un offset o rango de datos deseados. En el FE nuestra responsabilidad es mostrar unos controles adecuados (interfaz de paginación) y gestionar las llamadas a petición de cambio de página para siempre limitar la cantidad de DOM renderizado evitando así una sobrecarga del DOM y, por lo tanto, problemas de rendimiento.
Existe una técnica llamada Virtualización que gestiona cuántos elementos de una lista mantenemos vivos en el DOM . El concepto se basa en solo montar los elementos que estén dentro del viewport más un buffer determinado (para evitar falta de datos al hacer scroll) y, en cambio, desmontar del DOM todos aquellos elementos que estén fuera de la vista del usuario. De este modo podremos obtener lo mejor de los dos mundos, una experiencia integrada y un DOM liviano que evitará posibles errores de rendimiento. Con esta solución también podremos aprovechar que contamos con los datos en memoria para realizar búsquedas/filtrados sin necesidad de más llamadas al servidor.
Puedes consultar esta librería para aplicar Virtualización con React: React Virtualized.
Hay que tener en cuenta que cada caso de uso puede encontrar beneficios y/o perjuicios en ambos métodos, dependiendo de factores como capacidad de respuesta de la API, cantidad de datos, necesidad de filtros complejos, etc. Por ello es importante analizar cada caso con criterio.
⬆回到索引
useDebugValue是什麼?Nos permite mostrar un valor personalizado en la pestaña de React DevTools que nos permitirá depurar nuestro código.
import { useDebugValue } from 'react'
function useCustomHook ( ) {
const value = 'custom value'
useDebugValue ( value )
return value
} En este ejemplo, el valor personalizado que se muestra en la pestaña de React DevTools es custom value .
Aunque es útil para depurar, no se recomienda usar este hook en producción.
⬆回到索引
Profiler en React? El Profiler es un componente que nos permite medir el tiempo que tarda en renderizarse un componente y sus hijos.
import { Profiler } from 'react'
function App ( ) {
return (
< Profiler id = "App" onRender = { ( id , phase , actualDuration ) => {
console . log ( { id , phase , actualDuration } )
} } >
< Component />
</ Profiler >
)
} El componente Profiler recibe dos parámetros:
id : es un identificador único para el componenteonRender : es una función que se ejecuta cada vez que el componente se renderizaEsta información es muy útil para detectar componentes que toman mucho tiempo en renderizarse y optimizarlos.
⬆回到索引
React no expone el evento nativo del navegador. En su lugar, React crea un objeto sintético que se basa en el evento nativo del navegador llamado SyntheticEvent . Para acceder al evento nativo del navegador, debemos usar el atributo nativeEvent :
function Button ( { onClick } ) {
return < button onClick = { e => onClick ( e . nativeEvent ) } > Haz clic aquí </ button >
}⬆回到索引
En React, los eventos se registran en la fase de burbuja por defecto. Para registrar un evento en la fase de captura, debemos añadir Capture al nombre del evento:
function Button ( { onClick } ) {
return < button onClickCapture = { onClick } > Haz clic aquí </ button >
}⬆回到索引
Aunque puedes usar el método renderToString para renderizar el HTML en el servidor, este método es síncrono y bloquea el hilo principal. Para evitar que bloquee el hilo principal, debemos usar el método renderToPipeableStream :
let didError = false
const stream = renderToPipeableStream (
< App /> ,
{
onShellReady ( ) {
// El contenido por encima de los límites de Suspense ya están listos
// Si hay un error antes de empezar a hacer stream, mostramos el error adecuado
res . statusCode = didError ? 500 : 200
res . setHeader ( 'Content-type' , 'text/html' )
stream . pipe ( res )
} ,
onShellError ( error ) {
// Si algo ha ido mal al renderizar el contenido anterior a los límites de Suspense, lo indicamos.
res . statusCode = 500
res . send (
'<!doctype html><p>Loading...</p><script src="clientrender.js"></script>'
)
} ,
onAllReady ( ) {
// Si no quieres hacer streaming de los datos, puedes usar
// esto en lugar de onShellReady. Esto se ejecuta cuando
// todo el HTML está listo para ser enviado.
// Perfecto para crawlers o generación de sitios estáticos
// res.statusCode = didError ? 500 : 200
// res.setHeader('Content-type', 'text/html')
// stream.pipe(res)
} ,
onError ( err ) {
didError = true
console . error ( err )
} ,
}
)⬆回到索引
renderToStaticNodeStream() y renderToPipeableStream() ? renderToStaticNodeStream() devuelve un stream de nodos estáticos, esto significa que no añade atributos extras para el DOM que React usa internamente para poder lograr la hidratación del HTML en el cliente. Esto significa que no podrás hacer el HTML interactivo en el cliente, pero puede ser útil para páginas totalmente estáticas.
renderToPipeableStream() devuelve un stream de nodos que contienen atributos del DOM extra para que React pueda hidratar el HTML en el cliente. Esto significa que podrás hacer el HTML interactivo en el cliente pero puede ser más lento que renderToStaticNodeStream() .
⬆回到索引
useDeferredValue鉤? El hook useDeferredValue nos permite renderizar un valor con una prioridad baja. Esto es útil para renderizar un valor que no es crítico para la interacción del usuario.
function App ( ) {
const [ text , setText ] = useState ( '¡Hola mundo!' )
const deferredText = useDeferredValue ( text , { timeoutMs : 2000 } )
return (
< div className = 'App' >
{ /* Seguimos pasando el texto actual como valor del input */ }
< input value = { text } onChange = { handleChange } />
...
{ /* Pero la lista de resultados se podría renderizar más tarde si fuera necesario */ }
< MySlowList text = { deferredText } />
</ div >
)
}⬆回到索引
renderToReadableStream()方法是什麼? Este método es similar a renderToNodeStream , pero está pensado para entornos que soporten Web Streams como Deno .
Un ejemplo de uso sería el siguiente:
const controller = new AbortController ( )
const { signal } = controller
let didError = false
try {
const stream = await renderToReadableStream (
< html >
< body > Success </ body >
</ html > ,
{
signal ,
onError ( error ) {
didError = true
console . error ( error )
}
}
)
// Si quieres enviar todo el HTML en vez de hacer streaming, puedes usar esta línea
// Es útil para crawlers o generación estática:
// await stream.allReady
return new Response ( stream , {
status : didError ? 500 : 200 ,
headers : { 'Content-Type' : 'text/html' } ,
} )
} catch ( error ) {
return new Response (
'<!doctype html><p>Loading...</p><script src="clientrender.js"></script>' ,
{
status : 500 ,
headers : { 'Content-Type' : 'text/html' } ,
}
)
}⬆回到索引
Para hacer testing de un componente, puedes usar la función render de la librería @testing-library/react . Esta función nos permite renderizar un componente y obtener el resultado.
import { render } from '@testing-library/react'
function Counter ( ) {
const [ count , setCount ] = useState ( 0 )
const increment = ( ) => setCount ( count + 1 )
return (
< div >
< p > Count: { count } </ p >
< button onClick = { increment } > Increment </ button >
</ div >
)
}
test ( 'Counter' , ( ) => {
const { getByText } = render ( < Counter /> )
expect ( getByText ( 'Count: 0' ) ) . toBeInTheDocument ( )
fireEvent . click ( getByText ( 'Increment' ) )
expect ( getByText ( 'Count: 1' ) ) . toBeInTheDocument ( )
} )⬆回到索引
Para hacer testing de un hook, puedes usar la función renderHook de la librería @testing-library/react-hooks . Esta función nos permite renderizar un hook y obtener el resultado.
import { renderHook } from '@testing-library/react-hooks'
function useCounter ( ) {
const [ count , setCount ] = useState ( 0 )
const increment = ( ) => setCount ( count + 1 )
return { count , increment }
}
test ( 'useCounter' , ( ) => {
const { result } = renderHook ( ( ) => useCounter ( ) )
expect ( result . current . count ) . toBe ( 0 )
act ( ( ) => {
result . current . increment ( )
} )
expect ( result . current . count ) . toBe ( 1 )
} )⬆回到索引
Flux es un patrón de arquitectura de aplicaciones que se basa en un unidireccional de datos. En este patrón, los datos fluyen en una sola dirección: de las vistas a los stores.
No es específico de React y se puede usar con cualquier librería de vistas. En este patrón, los stores son los encargados de almacenar los datos de la aplicación. Los stores emiten eventos cuando los datos cambian. Las vistas se suscriben a estos eventos para actualizar los datos.
Esta arquitectura fue creada por Facebook para manejar la complejidad de sus aplicaciones. Redux se basó en este patrón para crear una biblioteca de gestión de estado global.
⬆回到索引
Es un error bastante común en React y que puede parecernos un poco extraño si estamos empezando a aprender esta tecnología. Por suerte, es bastante sencillo de solucionar.
Básicamente, este mensaje aparece en la consola cuando estamos renderizando un listado dentro de nuestro componente, pero no le estamos indicando la propiedad "key". React usa esta propiedad para determinar qué elemento hijo dentro de un listado ha sufrido cambios, por lo que funciona como una especie de identificativo.
De esta manera, React utiliza esta información para identificar las diferencias existentes con respecto al DOM y optimizar la renderización del listado, determinando qué elementos necesitan volverse a calcular. Esto habitualmente pasa cuando agregamos, eliminamos o cambiamos el orden de los items en una lista.
Recomendamos revisar las siguientes secciones:
什麼是React中的列表渲染?
¿Por qué puede ser mala práctica usar el ´index´ como key en un listado de React?
⬆回到索引
Una de las reglas de los hooks de React es que deben llamarse en el mismo orden en cada renderizado. React lo necesita para saber en qué orden se llaman los hooks y así mantener el estado de los mismos internamente. Por ello, los hooks no pueden usarse dentro de una condición if , ni un loop, ni tampoco dentro de una función anónima. Siempre deben estar en el nivel superior de la función.
Por eso el siguiente código es incorrecto:
// código incorrecto por saltar las reglas de los hooks
function Counter ( ) {
const [ count , setCount ] = useState ( 0 )
// de forma condicional, creamos un estado con el hook useState
// lo que rompe la regla de los hooks
if ( count > 0 ) {
const [ name , setName ] = useState ( 'midu' )
}
return < div > { count } { name } </ div >
} También el siguiente código sería incorrecto, aunque no lo parezca, ya que estamos usando el segundo useState de forma condicional (pese a no estar dentro de un if ) ya que se ejecutará sólo cuando count sea diferente a 0 :
// código incorrecto por saltar las reglas de los hooks
function Counter ( ) {
const [ count , setCount ] = useState ( 0 )
// si count es 0, no se ejecuta el siguiente hook useState
// ya que salimos de la ejecución aquí
if ( count === 0 ) return null
const [ name , setName ] = useState ( 'midu' )
return < div > { count } { name } </ div >
}Ten en cuenta que si ignoras este error, es posible que tus componentes no se comporten de forma correcta y tengas comportamientos no esperados en el funcionamiento de tus componentes.
Para arreglar este error, como hemos comentado antes, debes asegurarte de que los hooks se llaman en el mismo orden en cada renderizado. El último ejemplo quedaría así:
function Counter ( ) {
const [ count , setCount ] = useState ( 0 )
// movemos el hook useState antes del if
const [ name , setName ] = useState ( 'midu' )
if ( count === 0 ) return null
return < div > { count } { name } </ div >
}Recomendamos revisar las siguientes secciones:
⬆回到索引
Este error se produce cuando intentamos actualizar el estado de un componente que ya no está montado. Esto puede ocurrir cuando el componente se desmonta antes de que se complete una petición asíncrona, por ejemplo:
function Movies ( ) {
const [ movies , setMovies ] = useState ( [ ] )
useEffect ( ( ) => {
fetchMovies ( ) . then ( ( ) => {
setMovies ( data . results )
} )
} )
if ( ! movies . length ) return null
return (
< section >
{ movies . map ( movie => (
< article key = { movie . id } >
< h2 > { movie . title } </ h2 >
< p > { movie . overview } </ p >
</ article >
) ) }
</ section >
)
} Parece un código inofensivo, pero imagina que usamos este componente en una página. Si el usuario navega a otra página antes de que se complete la petición, el componente se desmontará y React lanzará el error, ya que intentará ejecutar el setMovies en un componente (Movies) que ya no está montado.
Para evitar este error, podemos usar una variable booleana con useRef que nos indique si el componente está montado o no. De esta manera, podemos evitar que se ejecute el setMovies si el componente no está montado:
function Movies ( ) {
const [ movies , setMovies ] = useState ( [ ] )
const mounted = useRef ( false )
useEffect ( ( ) => {
mounted . current = true
fetchMovies ( ) . then ( ( ) => {
if ( mounted . current ) {
setMovies ( data . results )
}
} )
return ( ) => mounted . current = false
} )
// ...
} Esto soluciona el problema pero no evita que se haga la petición aunque el componente ya no esté montado . Para cancelar la petición y así ahorrar transferencia de datos, podemos abortar la petición usando la API AbortController :
function Movies ( ) {
const [ movies , setMovies ] = useState ( [ ] )
useEffect ( ( ) => {
// creamos un controlador para abortar la petición
const abortController = new AbortController ( )
// pasamos el signal al fetch para que sepa que debe abortar
fetchMovies ( { signal : abortController . signal } )
. then ( ( ) => {
setMovies ( data . results )
} ) . catch ( error => {
if ( error . name === 'AbortError' ) {
console . log ( 'fetch aborted' )
}
} )
return ( ) => {
// al desmontar el componente, abortamos la petición
// sólo funcionará si la petición sigue en curso
abortController . abort ( )
}
} )
// ...
}
// Debemos pasarle el parámetro signal al `fetch`
// para que enlace la petición con el controlador
const fetchMovies = ( { signal } ) => {
return fetch ( 'https://api.themoviedb.org/3/movie/popular' , {
signal // <--- pasamos el signal
} ) . then ( response => response . json ( ) )
} Sólo ten en cuenta la compatibilidad de AbortController en los navegadores. En caniuse puedes ver que no está soportado en Internet Explorer y versiones anteriores de Chrome 66, Safari 12.1 y Edge 16.
⬆回到索引
Este error indica que algo dentro de nuestro componente está generando muchos pintados que pueden desembocar en un loop (bucle) infinito. Algunas de las razones por las que puede aparecer este error son las siguientes:
function Counter ( ) {
const [ count , setCount ] = useState ( 0 )
// código incorrecto
// no debemos actualizar el estado de manera directa
setCount ( count + 1 )
return < div > { count } </ div >
} Lo que sucede en este ejemplo, es que al renderizarse el componente, se llama a la función setCount para actualizar el estado. Una vez el estado es actualizado, se genera nuevamente un render del componente y se repite todo el proceso infinitas veces.
Una posible solución sería:
function Counter ( ) {
// ✅ código correcto
// se pasa el valor inicial deseado en el `useState`
const [ count , setCount ] = useState ( 1 )
return < div > { count } </ div >
}Llamar directamente a una función en un controlador de eventos.
function Counter ( ) {
const [ count , setCount ] = useState ( 0 )
// código incorrecto
//se ejecuta directamente la función `setCount` y provoca un renderizado infinito
return < div >
< p > Contador: { count } </ p >
< button onClick = { setCount ( count + 1 ) } > Incrementar </ button >
</ div >
} En este código, se está ejecutando la función setCount que actualiza el estado en cada renderizado del componente, lo que provoca renderizaciones infinitas.
La manera correcta sería la siguiente:
function Counter ( ) {
const [ count , setCount ] = useState ( 0 )
// ✅ código correcto
// se pasa un callback al evento `onClick`
// esto evita que la función se ejecute en el renderizado
return < div >
< p > Contador: { count } </ p >
< button onClick = { ( ) => setCount ( count + 1 ) } > Incrementar </ button >
</ div >
} Usar incorrectamente el Hook de useEffect .
Al ver este ejemplo:
function Counter ( ) {
const [ count , setCount ] = useState ( 0 )
// código incorrecto
useEffect ( ( ) => {
setCounter ( counter + 1 )
} ) // ?️ no colocar el array de dependencias
return < div > { count } </ div >
} Lo que ocurre, es que al no colocar un array de dependencias en el hook de useEffect , estamos provocando que el código que se encuentre dentro se ejecute en cada renderizado del componente. Al llamar al setCounter y actualizar el estado, obtenemos nuevamente renderizaciones infinitas.
Para solucionarlo, podemos hacer lo siguiente:
function Counter ( ) {
const [ count , setCount ] = useState ( 0 )
// ✅ código correcto
// estamos indicando que sólo queremos que el código se ejecute una vez
useEffect ( ( ) => {
setCounter ( counter + 1 )
} , [ ] ) //colocamos un array de dependencias vacío.
return < div > { count } </ div >
}Estas son solo algunas de las posibles causas que podemos encontrar cuando nos topamos con este mensaje de error en el código. Si quieres complementar esta información, te recomendamos revisar las siguientes secciones:
⬆回到索引
El Shadow DOM es una API del navegador que nos permite crear un árbol de nodos DOM independiente dentro de un elemento del DOM. Esto nos permite crear componentes que no interfieran con el resto de la aplicación. Se usa especialmente con Web Components.
El Virtual DOM es una representación del DOM en memoria. Esta representación se crea cada vez que se produce un cambio en el DOM. Esto nos permite comparar el DOM actual con el DOM anterior y así determinar qué cambios se deben realizar en el DOM real. Lo usa React y otras bibliotecas para hacer el mínimo número de cambios en el DOM real.
⬆回到索引
En React, el Binding se refiere a la forma en que se relaciona y sincroniza el estado (state) de un componente con su vista (render) . El estado de un componente es un objeto que contiene información que puede ser utilizada para determinar cómo se debe mostrar el componente. Existen dos tipos de binding en React: One-Way Binding y Two-Way Binding .
One-Way Binding (Enlace unidireccional) :
En React se refiere a la capacidad de un componente para actualizar su estado (state) y su vista (render) de manera automática cuando cambia el estado, pero no permitiendo que la vista actualice el estado. En otras palabras, el one-way binding significa que el flujo de datos es unidireccional, desde el estado hacia la vista, y no al revés.
例如:
import React , { useState } from 'react' ;
function OneWayBindingExample ( ) {
const [ name , setName ] = useState ( 'midu' ) ;
return (
< div >
< p > Hello, { name } </ p >
< input
type = "text"
placeholder = "Enter your name"
onChange = { ( e ) => setName ( e . target . value ) }
/>
</ div >
) ;
}
export default OneWayBindingExample ;En este ejemplo, el componente tiene un estado inicial llamado name con el valor midu . La función setName se utiliza para actualizar el estado name cuando se produce un evento onChange en el input. Sin embargo, la vista (la linea que muestra Hello, {name} ) no tiene la capacidad de actualizar el estado name .
Two-Way Binding (Enlace bidireccional) :
Se refiere a la capacidad de un componente para actualizar su estado y su vista de manera automática tanto cuando cambia el estado como cuando se produce un evento en la vista. En otras palabras, el Two-Way Binding significa que el flujo de datos es bidireccional, desde el estado hacia la vista y desde la vista hacia el estado. Para lograr esto se utilizan en conjunto con los eventos, como onChange , para capturar la información de los inputs y actualizar el estado, React no proporciona un mecanismo nativo para two-way binding, pero se puede lograr utilizando librerías como react-forms o formik.
例如:
import React , { useState } from 'react' ;
function TwoWayBindingExample ( ) {
const [ name , setName ] = useState ( 'midu' ) ;
return (
< div >
< p > Hello, { name } </ p >
< input
type = "text"
placeholder = "Enter your name"
value = { name }
onChange = { ( e ) => setName ( e . target . value ) }
/>
</ div >
) ;
}
export default TwoWayBindingExample ;En este ejemplo, el componente tiene un estado inicial llamado name con el valor midu . La función setName se utiliza para actualizar el estado name cuando se produce un evento onChange en el input, y se puede ver reflejado en el valor del input. Sin embargo, en este caso se está utilizando el atributo value para que el valor del input sea actualizado con el valor del estado, es decir, se está actualizando tanto el estado como el input.
Por si no quedó claro:
En términos sencillos, el Binding en React puede compararse con una cafetera y una taza de café. El estado del componente sería la cafetera , y la vista del componente sería la taza de café .
En el caso del One-Way Binding , la cafetera solo puede verter café en una dirección, hacia la taza de café. Esto significa que la cafetera puede llenar automáticamente la taza de café con café fresco, pero la taza de café no puede devolver automáticamente el café a la cafetera. De esta manera, el estado del componente (la cafetera) puede actualizar automáticamente la vista (la taza de café) cuando cambia, pero la vista no puede actualizar automáticamente el estado .
En el caso del Two-Way Binding , la cafetera puede verter y recibir café en ambas direcciones, hacia y desde la taza de café (no sé por qué alguien necesitaría hacer algo así). Esto significa que la cafetera puede llenar y vaciar automáticamente la taza de café con café fresco. De esta manera, tanto el estado del componente como la vista pueden actualizarse automáticamente entre sí.
Sí quieres saber más comparto el siguiente enlace:
How To Bind Any Component to Data in React: One-Way Binding
⬆回到索引