0에서 전문가로. 스페인어로 된 자세한 답변으로 ??
프로젝트가 마음에 들면 떠나십시오.
Twitch 프로그래밍 스트림 : twitch.tv/midudev
Discord Development Community : Discord.gg/midudev
children 은 반응에 무엇이 있습니까?class 사용할 수없는 이유는 무엇입니까?useState ?useEffect 무엇을합니까?useEffect 의 사용 사례를 설명하십시오useEffect 에서 이벤트를 구독하는 방법useId 무엇을합니까?useEffect 가질 수 있습니까?useEffect 에서 API에 대한 요청을 올바르게 취소 할 수 있습니까?useEffect 와 useLayoutEffect 의 차이점은 무엇입니까?if 사용할 수없는 이유는 무엇입니까?index 키로 사용하는 것이 나쁜 관행 일 수 있습니까?useMemo 무엇입니까?useMemo 하는 것이 좋습니다.useCallback 무엇입니까?useCallback 사용하여 구성 요소를 최적화하는 것이 좋습니다.useCallback 과 useMemo 의 차이점은 무엇입니까?useRef 어떻게 작동합니까?useLayoutEffect 무엇을합니까?useLayoutEffect 실행 순서StrictMode 는 무엇입니까?SyntheticEvent 무엇입니까?flushSync 무엇입니까?useImperativeHandle HookeHandle의 사용은 무엇입니까?cloneElement 방법은 무엇입니까?StrictMode 응용 프로그램의 두 배를 렌더링하는 이유는 무엇입니까?useEffect 함께 페치 청원을 어떻게 중단 할 수 있습니까?useDebugValue 는 무엇입니까?Profiler 무엇입니까?renderToStaticNodeStream() 과 renderToPipeableStream() 의 차이점은 무엇입니까?useDeferredValue 훅은 무엇입니까?renderToReadableStream() 메소드는 무엇입니까?React는 사용자 인터페이스를 구축하기위한 오픈 소스 JavaScript 라이브러리입니다. 그것은 UI의 화합물을 기반으로합니다. 인터페이스는 자체 상태를 포함하는 독립적 인 성분으로 나뉩니다. 구성 요소의 상태가 변경되면 다시 반응하여 인터페이스를 다시 렌더링합니다.
이로 인해 반응은 복잡한 인터페이스를 구축하는 데 매우 유용한 도구가됩니다. 인터페이스를 작고 재사용 가능한 조각으로 나눌 수 있기 때문입니다.
2011 년 Facebook에서 일하고 복잡한 사용자 인터페이스를 만드는 방법을 단순화하고자하는 소프트웨어 엔지니어 인 Jordan Walke가 2011 년에 만들었습니다.
매우 인기있는 도서관이며 Facebook, Netflix, Airbnb, Twitter, Instagram 등과 같은 많은 회사에서 사용합니다.
관심사 :
back 인덱스로 돌아갑니다
React의 주요 특성은 다음과 같습니다.
구성 요소 : React는 UI의 화합물을 기반으로합니다. 인터페이스는 자체 상태를 포함하는 독립적 인 구성 요소로 나뉩니다. 구성 요소의 상태가 변경되면 다시 반응하여 인터페이스를 다시 렌더링합니다.
가상 DOM : React는 가상 DOM을 사용하여 구성 요소를 렌더링합니다. 가상 Dom은 실제 DOM 표현입니다. 구성 요소의 상태가 변경되면 다시 반응하여 인터페이스를 다시 렌더링합니다. Real DOM을 수정하는 대신 REACT는 가상 DOM을 수정 한 다음 가상 DOM을 실제 DOM과 비교합니다. 이런 식으로 React는 실제 Dom에 어떤 변화를 적용 해야하는지 알고 있습니다.
선언 : RECT는 선언적이므로 작업을 수행하는 방법이 아니라 수행해야 할 작업이 지정되지 않습니다. 이로 인해 코드는 이해하고 유지하기가 더 쉽습니다.
단방향 : React는 단방향이므로 데이터가 한 방향으로 흐릅니다. 데이터는 부모에서 어린이 구성 요소로 흐릅니다.
Universal : React는 클라이언트와 서버에서 실행할 수 있습니다. 또한 React Native를 사용하여 Android 및 iOS 용 기본 응용 프로그램을 생성 할 수 있습니다.
back 인덱스로 돌아갑니다
명령에 따라 인터페이스를 렌더링하는 방법을 알려주지 않습니다. 우리는 당신에게 렌더링하고 반응 할 내용을 알려줍니다.
선언과 명령 사이의 예 :
// Declarativo
const element = < h1 > Hello, world </ h1 >
// Imperativo
const element = document . createElement ( 'h1' )
element . innerHTML = 'Hello, world'back 인덱스로 돌아갑니다
구성 요소는 인터페이스의 일부를 렌더링하는 코드입니다. 구성 요소는 매개 변수화, 재사용 및 자체 상태를 포함 할 수 있습니다.
반응에서 구성 요소는 함수 또는 클래스를 사용하여 생성됩니다.
back 인덱스로 돌아갑니다
RECT JSX를 사용하여 렌더링해야 할 사항을 선언합니다. JSX는 HTML에 시각적으로 더 가까운 코드를 작성할 수있는 JavaScript의 확장으로 코드의 가독성을 향상시키고 이해하기 쉽습니다.
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 >
}두 코드 모두 동일합니다.
back 인덱스로 돌아갑니다
JSX는 트랜스 필터 또는 컴파일러를 사용하여 브라우저에서 호환되는 JavaScript 코드로 변환됩니다 . 가장 유명한 것은 오늘날의 바벨 (Babel)입니다.이 바벨은 일련의 플러그인을 사용하여 변환과 호환되지만 SWC와 같은 다른 플러그인이 있습니다.
JSX가 Babel 코드 놀이터로 어떻게 변형되는지 알 수 있습니다.
트랜스 필터가 필요하지 않은 특별한 경우가 있습니다. 예를 들어, JSX 구문에 대한 기본 지원이 있으며 코드를 호환하여 코드를 변환 할 필요는 없습니다.
back 인덱스로 돌아갑니다
구성 요소는 소품을 받고 요소를 반환하는 함수 또는 클래스입니다. 요소는 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!'
}
}back 인덱스로 돌아갑니다
React의 구성 요소는 React의 요소를 반환하는 기능 또는 클래스입니다. 오늘날 가장 권장되는 것은 기능을 사용하는 것입니다.
function HelloWorld ( ) {
return < h1 > Hello World! </ h1 >
}그러나 클래스를 사용하여 React 구성 요소를 만들 수도 있습니다.
import { Component } from 'react'
class HelloWorld extends Component {
render ( ) {
return < h1 > Hello World! </ h1 >
}
}중요한 것은 함수 또는 클래스의 이름이 대문자로 시작한다는 것입니다. 이는 반응하여 구성 요소와 HTML 요소를 구별하기 위해 필요합니다.
back 인덱스로 돌아갑니다
소품은 구성 요소의 특성입니다. 그들은 한 구성 요소에서 다른 구성 요소로 전달되는 데이터입니다. 예를 들어, 버튼을 표시하는 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" / >소품은 기능과 마찬가지로 구성 요소를 매개 변수화하는 방법입니다. 우리는 모든 유형의 데이터를 구성 요소, 심지어 다른 구성 요소에 전달할 수 있습니다.
back 인덱스로 돌아갑니다
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에서 재사용 가능한 구성 요소를 만드는 데 매우 중요합니다.
back 인덱스로 돌아갑니다
소품은 아버지 구성 요소의 주장으로 아동 구성 요소에 전달되는 대상입니다. 그것들은 불변이며 아동 구성 요소에서 수정할 수 없습니다.
상태는 구성 요소 내에서 정의 된 값입니다. 그 가치는 불변 (직접 수정 될 수 없음)이지만 (직접 수정 될 수는 없음) 국가의 새로운 값이 설정되어 반응이 구성 요소를 재확인 할 수 있습니다.
따라서 상태는 구성 요소의 렌더링에 영향을 미치며 관리가 다릅니다.
back 인덱스로 돌아갑니다
그렇습니다.주는 소품의 가치로 초기화 될 수 있습니다. 그러나 자체 변경이 있으면 상태가 자동으로 업데이트되지 않을 것입니다. 구성 요소가 처음으로 장착 될 때 상태가 한 번 초기화되기 때문입니다.
예를 들어 기능 구성 요소 :
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 ) }
/>
)
}국가가 업데이트 할 것이라고 생각하는 것은 매우 일반적인 실수이므로 고려하십시오.
back 인덱스로 돌아갑니다
조건부 렌더링은 조건에 따라 한 구성 요소 또는 다른 구성 요소를 표시하는 방법입니다.
React에서 조건부 렌더링을 만들기 위해 Ternary Operator를 사용합니다.
function Button ( { text } ) {
return text
? < button > { text } </ button >
: null
} 이 경우 text 존재하면 버튼이 렌더링됩니다. 존재하지 않으면 아무것도 렌더링되지 않습니다.
&& 운영자와의 조건부 렌더링 구현을 찾는 것이 일반적입니다.
function List ( { listArray } ) {
return listArray ?. length && listArray . map ( item => item )
} length 양수 (0에서 0)라면지도를 페인트합니다. ! 글쎄! 브라우저 a 0에 페인트되기 때문에 length 제로가 있으면 조심하십시오.
3 대 연산자를 사용하는 것이 바람직합니다. Kent C. Dodds는 주제에 대해 흥미로운 기사를 가지고 있습니다. jsx에서 && 대신 사용하십시오
back 인덱스로 돌아갑니다
class 사용할 수없는 이유는 무엇입니까? React의 구성 요소에 CSS 클래스를 적용하려면 className 사용합니다.
function Button ( { text } ) {
return (
< button className = "button" >
{ text }
</ button >
)
} className 이라는 이유는 class JavaScript로 예약 된 단어이기 때문입니다. 따라서 JSX에서는 CSS 클래스를 적용하기 위해 className 사용해야합니다.
back 인덱스로 돌아갑니다
온라인 CSS 스타일을 React의 구성 요소에 적용하기 위해 우리는 style 사용합니다. HTML로 수행하는 방법의 차이점은 React에서 스타일이 텍스트 체인이 아닌 객체로 전달된다는 것입니다 (이것은 이중 사각형 브래킷으로 더 명확 할 수 있으며, 첫 번째는 JavaScript 표현식이며, 객체를 생성하는 데 초를 나타냅니다).
function Button ( { text } ) {
return (
< button style = { { color : 'red' , borderRadius : '2px' } } >
{ text }
</ button >
)
}또한 CSS 속성의 이름은 Camelcase에 있습니다.
back 인덱스로 돌아갑니다
style 와 3 원 운영자를 사용하여 React에 구성 요소가있는 스타일을 적용 할 수 있습니다.
function Button ( { text , primary } ) {
return (
< button style = { { color : primary ? 'red' : 'blue' } } >
{ text }
</ button >
)
} 이전의 경우 primary true 인 경우 버튼은 색상이 빨간색됩니다. 그렇지 않다면, 색상 파란색이 있습니다.
클래스를 사용하여 동일한 역학을 따를 수도 있습니다. 이 경우, 우리는 3 원 운영자를 사용하여 클래스를 추가할지 여부를 결정합니다.
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 true 인 경우 primary 클래스가 버튼에 추가됩니다. 그렇지 않은 경우 추가되지 않습니다. 대신 button 클래스가 항상 추가됩니다.
back 인덱스로 돌아갑니다
목록 렌더링은 다양한 요소를 반복하고 각 요소에 대한 반응 요소를 렌더링하는 방법입니다.
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 있습니다. 이는 반응이 목록의 각 요소를 식별하고 효율적으로 업데이트하는 데 필요합니다. 나중에 이것에 대한 더 자세한 설명이 있습니다.
back 인덱스로 돌아갑니다
구성 요소 렌더링 외부에 주석을 작성하려면 문제없이 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 >
)
}back 인덱스로 돌아갑니다
React의 구성 요소에 이벤트를 추가하려면 구문 on 사용하고 Camelcase 의 기본 브라우저 이벤트 이름을 사용합니다.
function Button ( { text , onClick } ) {
return (
< button onClick = { onClick } >
{ text }
</ button >
)
} 이 경우 Button 구성 요소는 함수 인 onClick 수신합니다. 사용자가 버튼을 클릭하면 onClick 기능이 실행됩니다.
back 인덱스로 돌아갑니다
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 >
)
}back 인덱스로 돌아갑니다
상태는 시간이 지남에 따라 변경 될 수있는 데이터를 포함하는 객체입니다. React에서 상태는 인터페이스의 변화를 제어하는 데 사용됩니다.
개념을 이해하려면 룸 스위치에 대해 생각해보십시오. 이 스위치에는 일반적으로 두 개의 상태가 있습니다. 스위치를 활성화하고 on 빛이 켜지고 빛을 off 빛이 꺼집니다.
이 동일한 개념은 사용자 인터페이스에 적용될 수 있습니다. 예를 들어, 나는 Facebook의 버튼을 좋아합니다. 사용자가 그렇게하지 않았을 때 사용자가 false 좋아했을 때 meGusta 의 상태를 가질 true 입니다.
우리는 State Boolean 값으로 가질 수있을뿐만 아니라 객체, 배열, 숫자 등을 가질 수 있습니다.
예를 들어, 카운터를 표시하는 Counter 구성 요소가있는 경우 상태를 사용하여 카운터 값을 제어 할 수 있습니다.
React에서 상태를 만들려면 Hook 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 >
)
}
}back 인덱스로 돌아갑니다
후크는 기능으로 생성 된 구성 요소에서 상태 및 기타 반응 특성을 가질 수있는 React API입니다.
이것은 이전에 불가능했으며 서점의 모든 가능성에 액세스하기 위해 class 구성 요소를 만들도록 강요했습니다.
후크는 후크이며 정확하게 그들이하는 일은 반응이 제공하는 모든 특성에 기능 구성 요소를 연결할 수 있다는 것입니다.
back 인덱스로 돌아갑니다
useState ? 후크 useState 상태 변수를 생성하는 데 사용되며, 값은 동적이며 시간이 지남에 따라 변경 될 수 있으며 사용되는 구성 요소의 리드 리드가 필요하다는 것을 의미합니다.
매개 변수 수신 :
두 가지 변수로 배열을 반환합니다.
setIsOpen(isOpen => !isOpen) 이 예에서는 count 값이 0으로 초기화되는 방법을 보여 주며 버튼 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 >
</ >
)
}back 인덱스로 돌아갑니다
여러 구성 요소가 동일한 상태 데이터를 공유 해야하는 경우 해당 상태를 가장 가까운 공통 조상과 공유하는 것이 좋습니다.
그렇지 않으면. 두 자녀가 아버지와 동일한 데이터를 공유한다면, 국가는 자녀의 지역 상태를 유지하는 대신 아버지에게 이동합니다.
이해하기 위해 가장 좋은 점은 우리가 예를 들어 본다는 것입니다. 우리가 원하는 선물 목록을 가지고 있다고 상상해보십시오. 그리고 선물을 추가하고 목록에 총 선물을 보여주고 싶다고 상상해보십시오.
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 에 업로드해야하며 Prop과 같은 선물 수를 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 구성 요소의 선물 수를 제안한 것으로 전달했습니다.
back 인덱스로 돌아갑니다
useEffect 무엇을합니까? Hook 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 >
</ >
)
}back 인덱스로 돌아갑니다
useEffect 의 사용 사례를 설명하십시오 우리는 다음과 같은 다른 방식으로 후크 useEffect 사용할 수 있습니다.
resize 와 같은 브라우저 이벤트를 구독하여 사용자가 창 크기를 변경시기를 알 수 있습니다.back 인덱스로 돌아갑니다
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 >
)
}back 인덱스로 돌아갑니다
useId 무엇을합니까? useId 는 HTML 레이블의 속성으로 전달할 수있는 고유 식별자를 생성하는 후크입니다. 특히 접근성에 유용합니다.
고유 한 ID를 생성하기 위해 구성 요소의 상위 레벨에있는 llama useId :
import { useId } from 'react'
function PasswordField ( ) {
const passwordHintId = useId ( )
// ...다음으로, 다른 속성으로 생성 된 ID는 다음과 같습니다.
< >
< input type = "password" aria-describedby = { passwordHintId } />
< p id = { passwordHintId } >
</ > aria-describedby 레이블을 사용하면 두 개의 레이블이 서로 관련되어 있음을 지정할 수 있으므로 PasswordField 화면에 여러 번 나타나더라도 생성 된 식별이 충돌하지 않는 USEID와 고유 한 식별을 생성 할 수 있습니다.
전체 예는 다음과 같습니다.
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 에서 볼 수 있듯이 구성 요소를 두 번 사용하고 있습니다. 예 password 들어 ID를 손으로 배치하면 ID가 고유하지 않으며 복제됩니다. 그렇기 때문에 useId 사용하여 ID를 자동으로 생성하는 것이 중요합니다.
back 인덱스로 돌아갑니다
의존성을 전달하지 않고 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 >
)
}back 인덱스로 돌아갑니다
조각은 DOM에 추가 요소를 추가하지 않고 요소를 그룹화하는 방법입니다. RECT는 여러 요소를 구성 요소에서 반환 할 수 없기 때문에 루트 요소 만 가능합니다.
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 >
</ >
)
}back 인덱스로 돌아갑니다
여러 요소를 포장 할 때 div 대신 조각을 사용하는 것이 바람직한 이유는 다음과 같습니다.
div DOM에 추가 요소를 추가하는 반면 조각 번호. 이것은 HTML 요소의 수와 DOM의 깊이가 더 낮습니다.div 사용하는 경우 요소 정렬에 문제가있을 수 있습니다.div 보다 빠릅니다.div 기본적으로 CSS를 적용합니다 ( display: block 적용 할 때 div 블록으로 동작하는 것을 만듭니다). 단편은 기본 스타일을 적용하지 않습니다.back 인덱스로 돌아갑니다
단일 목표를 가진 부모 구성 요소를 작성하는 구성 요소 디자인 패턴으로, 자녀에게 문제없이 렌더링하는 데 필요한 속성을 제공합니다.
새로운 구성 요소를 구축 할 때 선언적 구조를 허용하며 단순성과 청소를위한 코드를 읽는 데 도움이됩니다.
이 디자인의 예는 어린이를 렌더링하는 목록입니다.
< 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의 컴파운드 구성 요소
Kent C. Dodds의 복합 구성 요소 수업은 영어로됩니다
back 인덱스로 돌아갑니다
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오늘 가장 인기 있고 권장되는 옵션은 Nextjs입니다. NPM 트렌드 소스
그들 각각은 웹 응용 프로그램 패키지입니다. 이들은 프로젝트의 종속성을 해결하고 각 변경마다 자동으로 새로 고침되는 개발 환경을 높이고 필요한 모든 정적 파일 등으로 제작을위한 응용 프로그램을 포장하는 개발 환경을 높입니다.
back 인덱스로 돌아갑니다
REACT DOM은 브라우저의 React 구성 요소를 렌더링하는 것을 담당하는 서점입니다. React는 다른 환경 (모바일 장치, 데스크탑 앱, 터미널 ...)에서 사용할 수있는 라이브러리입니다.
REACT 라이브러리는 건조한 반면, 구성 요소 제작 엔진, 후크, 소품 시스템 및 상태입니다 ... React DOM은 브라우저에서 특별히 React 구성 요소를 렌더링하는 데 도움이되는 서점입니다.
예를 들어, 원시는 동일하지만 모바일 장치의 경우에도 동일하게 수행됩니다.
back 인덱스로 돌아갑니다
반응을 배우고 지배하려면 JavaScript를 알아야합니다. 자체 DSL (도메인 별 언어)을 기반으로하는 Angular 및 VUE 와 같은 다른 프레임 워크 및 라이브러리와 달리 React는 JSX 라는 JavaScript 구문의 확장을 사용합니다. 나중에 우리는 그것을 자세히 볼 수 있지만 결국, 덜 자바 스크립트를 작성하는 것은 여전히 구문 설탕입니다.
반응에서 모든 것은 JavaScript입니다. 더 좋고 나쁘게. 이 책은 프로그래밍 언어에 대한 사전 지식을 제거하지만 시작하기 전에 우리는 당신이 알아야 할 가장 중요한 특성에 대해 작은 검토를 할 것입니다.
이미 JavaScript를 지배하고 있다면이 장을 건너 뛰고 책을 계속할 수 있지만이 장을 항상 참조로 검토 할 수 있습니다.
ECMAScript 모듈은 JavaScript가 다른 파일간에 변수, 함수 및 클래스를 가져와야하는 기본 형식입니다. 오늘날, 특히 우리가 애플리케이션 패키지를 웹 팩으로 작업하면이 구문 작업을 지속적으로 작업 할 것입니다.
한편으로는 모듈을 기본적으로 내보내어 모듈을 만들 수 있습니다.
// 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' )
} )
} )Dynamic Import는 Webpack 또는 Vite와 같은 PackagingR을 사용하면 유용합니다. 이는 일반 번들에서로드 될 덩어리 (조각)를 생성하기 때문입니다. 목표? 응용 프로그램 성능을 향상시킵니다.
모듈로 작업 할 구문이 더 많지만, 우리가 본 것을 아는 것만으로 이미이 책을 따르기에 충분할 것입니다.
왜 중요한가요?
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에는 구성 요소와 후크의 두 가지 매우 중요한 개념이 있습니다. 우리는 지금 그들에게 자세히 설명하지 않을 것이지만 중요한 것은 둘 다 기능으로 만들어 졌다는 것입니다.
Poder añadir valores por defecto a los parámetros de esas funciones en el caso que no venga ningún argumento es clave para poder controlar React con éxito.
Los componentes, por ejemplo, pueden no recibir parámetros y, pese a ello, seguramente vas a querer que tengan algún comportamiento por defecto. Lo podrás conseguir de esta forma.
Los template literals o plantillas de cadenas llevan las cadenas de texto al siguiente nivel permitiendo expresiones incrustadas en ellas.
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 } `Como ves, para poder usar los template literals, necesitas usar el símbolo ```
Además, nos permiten utilizar cadenas de texto de más de una línea.
왜 중요한가요?
En React esto se puede utilizar para diferentes cosas. No sólo es normal crear cadenas de texto para mostrar en la interfaz... también puede ser útil para crear clases para tus elementos HTML de forma dinámica. Verás que los template literales están en todas partes.
Desde ECMAScript 2015 se puede iniciar un objeto utilizado nombre de propiedades abreviadas. Esto es que si quieres utilizar como valor una variable que tiene el mismo nombre que la key, entonces puedes indicar la inicialización una vez:
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 }왜 중요한가요?
En React se trata muchas veces con objetos y siempre vamos a querer escribir el menor número de líneas posible para mantener nuestro código fácil de mantener y entender.
La sintaxis de desestructuración es una expresión de JavaScript que permite extraer valores de Arrays o propiedades de objetos en distintas variables.
// 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 )왜 중요한가요?
En React hay mucho código básico que da por sentado que conoces y dominas esta sintaxis. Piensa que los objetos y los arreglos son tipos de datos que son perfectos para guardar datos a representar en una interfaz. Así que poder tratarlos fácilmente te va a hacer la vida mucho más fácil.
Saber manipular arreglos en JavaScript es básico para considerar que se domina. Cada método realiza una operación en concreto y devuelve diferentes tipos de datos. Todos los métodos que veremos reciben un callback (función) que se ejecutará para cada uno de los elementos del array.
Vamos a revisar algunos de los métodos más usados:
// 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왜 중요한가요?
En React es muy normal almacenar los datos que tenemos que representar en la UI como array. Esto hace que muchas veces necesitemos tratarlos, filtrarlos o extraer información de ellos. Es primordial entender, conocer y dominar al menos estos métodos, ya que son los más usados.
La sintaxis de spread nos permite expandir un iterable o un objeto en otro lugar dónde se espere esa información. Para poder utilizarlo, necesitamos utilizar los tres puntos suspensivos ... justo antes.
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' ]Esto mismo lo podemos conseguir con un objeto, de forma que podemos expandir todas sus propiedades en otro objeto de forma muy sencilla.
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' ]
// }Es importante notar que esto hace una copia, sí, pero superficial. Si tuviéramos objetos anidados dentro del objeto entonces deberíamos tener en cuenta que podríamos mutar la referencia. Veamos un ejemplo.
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' }
// }왜 중요한가요?
En React es muy normal tener que añadir nuevos elementos a un array o crear nuevos objetos sin necesidad de mutarlos. El operador Rest nos puede ayudar a conseguir esto. Si no conoces bien el concepto de valor y referencia en JavaScript, sería conveniente que lo repases.
La sintaxis ... hace tiempo que funciona en JavaScript en los parámetros de una función. A esta técnica se le llamaba parámetros rest y nos permitía tener un número indefinido de argumentos en una función y poder acceder a ellos después como un array.
function suma ( ... allArguments ) {
return allArguments . reduce ( ( previous , current ) => {
return previous + current
} )
}Ahora el operador rest también se puede utilizar para agrupar el resto de propiedades un objeto o iterable. Esto puede ser útil para extraer un elemento en concreto del objeto o el iterable y crear una copia superficial del resto en una nueva variable.
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'
// }
// }También podría funcionar con arrays:
const [ firstNumber , ... restOfNumbers ] = [ 1 , 2 , 3 ]
console . log ( firstNumber ) // -> 1
console . log ( restOfNumbers ) // -> [2, 3]왜 중요한가요?
Es una forma interesante de eliminar (de forma figurada) una propiedad de un objeto y creando una copia superficial del resto de propiedades. A veces puede ser interesante para extraer la información que queremos de unos parámetros y dejar el resto en un objeto que pasaremos hacia otro nivel.
El operador de encadenamiento opcional ?. te permite leer con seguridad el valor de una propiedad que está anidada dentro de diferentes niveles de un objeto.
De esta forma, en lugar de revisar si las propiedades existen para poder acceder a ellas, lo que hacemos es usar el encadenamiento opcional.
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왜 중요한가요?
Un objeto es una estructura de datos que es perfecta a la hora de representar muchos elementos de la UI. ¿Tienes un artículo? Toda la información de un artículo seguramente la tendrás representada en un objeto.
Conforme tu UI sea más grande y compleja, estos objetos tendrán más información y necesitarás dominar el encadenamiento opcional ?. para poder acceder a su información con garantías.
⬆ Volver a índice
Un hook personalizado es una función que empieza con la palabra use y que puede utilizar otros hooks. Son ideales para reutilizar lógica en diferentes componentes. Por ejemplo, podemos crear un hook personalizado para extraer la gestión del estado de un contador:
// ./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 }
}Para usarlo en un componente:
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 >
</ >
)
}⬆ Volver a índice
useEffect puede tener un componente? Aunque normalmente los componentes de React solo cuentan con un useEffect lo cierto es que podemos tener tantos useEffect como queramos en un componente. Cada uno de ellos se ejecutará cuando se renderice el componente o cuando cambien las dependencias del efecto.
⬆ Volver a índice
Podemos ejecutar código cuando el componente se desmonta usando el hook useEffect y dentro devolver una función con el código que queremos ejecutar. En este caso, la función que se pasa como primer parámetro del useEffect se ejecutará cuando el componente se monte, y la función que es retornada se ejecutará cuando se desmonte.
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 >
}Esto es muy útil para limpiar recursos que se hayan creado en el componente, como por ejemplo, eventos del navegador o para cancelar peticiones a APIs.
⬆ Volver a índice
useEffect correctamente Cuando hacemos una petición a una API, podemos cancelarla para evitar que se ejecute cuando el componente se desmonte usando AbortController como hacemos en este ejemplo:
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 ( )
} , [ ] ) Esto también funciona con 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 ( )
} , [ ] )⬆ Volver a índice
Los hooks en React tienen dos reglas fundamentales:
⬆ Volver a índice
useEffect y useLayoutEffect ?Aunque ambos son muy parecidos, tienen una pequeña diferencia en el momento en el que se ejecutan.
useLayoutEffect se ejecuta de forma síncrona inmediatamente después que React haya actualizado completamente el DOM tras el renderizado. Puede ser útil si necesitas recuperar un elemento del DOM y acceder a sus dimensiones o posición en pantalla.
useEffect se ejecuta de forma asíncrona tras el renderizado, pero no asegura que el DOM se haya actualizado. Es decir, si necesitas recuperar un elemento del DOM y acceder a sus dimensiones o posición en pantalla, no podrás hacerlo con useEffect porque no tienes la garantía de que el DOM se haya actualizado.
Normalmente, el 99% de las veces, vas a querer utilizar useEffect y, además, tiene mejor rendimiento, ya que no bloquea el renderizado.
⬆ Volver a índice
Desde que en React 16.8.0 se incluyeron los hooks, los componentes de funciones pueden hacer casi todo lo que los componentes de clase.
Aunque no hay una respuesta clara a esta pregunta, normalmente los componentes funcionales son más sencillos de leer y escribir y pueden tener un mejor rendimiento en general.
Además, los hooks solo se pueden usar en los componentes funcionales . Esto es importante, ya que con la creación de custom hooks podemos reutilizar la lógica y podría simplificar nuestros componentes.
Por otro lado, los componentes de clase nos permiten usar el ciclo de vida de los componentes, algo que no podemos hacer con los componentes funcionales donde solo podemos usar useEffect .
참조 :
⬆ Volver a índice
Los componentes puros son aquellos que no tienen estado y que no tienen efectos secundarios. Esto quiere decir que no tienen ningún tipo de lógica que no sea la de renderizar la interfaz.
Son más fáciles de testear y de mantener. Además, son más fáciles de entender porque no tienen lógica compleja.
Para crear un componente puro en React usamos una function:
function Button ( { text } ) {
return (
< button >
{ text }
</ button >
)
} En este caso, el componente Button recibe una prop text que es un string. El componente Button renderiza un botón con el texto que recibe en la prop text .
⬆ Volver a índice
Cuando renderizamos nuestra aplicación en el servidor, React genera un HTML estático. Este HTML estático es simplemente un string que contiene el HTML que se va a mostrar en la página.
Cuando el navegador recibe el HTML estático, lo renderiza en la página. Sin embargo, este HTML estático no tiene interactividad. No tiene eventos, no tiene lógica, no tiene estado, etc. Podríamos decir que no tiene vida .
Para hacer que este HTML estático pueda ser interactivo, React necesita que el HTML estático se convierta en un árbol de componentes de React. Esto se llama hidratación .
De esta forma, en el cliente, React reutiliza este HTML estático y se dedica a adjuntar los eventos a los elementos, ejecutar los efectos que tengamos en los componentes y conciliar el estado de los componentes.
⬆ Volver a índice
El Server Side Rendering es una técnica que consiste en renderizar el HTML en el servidor y enviarlo al cliente. Esto nos permite que el usuario vea la interfaz de la aplicación antes de que se cargue el JavaScript.
Esta técnica nos permite mejorar la experiencia de usuario y mejorar el SEO de nuestra aplicación.
⬆ Volver a índice
Para crear un Server Side Rendering con React desde cero podemos usar el paquete react-dom/server que nos permite renderizar componentes de React en el servidor.
Veamos un ejemplo de cómo crear un Server Side Rendering con React desde cero con 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 )
} ) Esto nos devolverá el HTML de la aplicación al acceder a la ruta / .
< h1 data-reactroot ="" > Hola mundo </ h1 >⬆ Volver a índice
Igual que las funciones en JavaScript, los componentes de React también pueden tener side effects (efectos colaterales). Un efecto colateral significa que el componente manipula o lee información que no está dentro de su ámbito.
Aquí puedes ver un ejemplo simple de un componente que tiene un efecto colateral. Un componente que lee y modifica una variable que está fuera del componente. Esto hace que sea imposible saber qué renderizará el componente cada vez que se use, ya que no sabemos el valor que tendrá count :
let count = 0
function Counter ( ) {
count = count + 1
return (
< p > Contador: { count } </ p >
)
}
export default function Counters ( ) {
return (
< >
< Counter />
< Counter />
< Counter />
</ >
)⬆ Volver a índice
A la hora de trabajar con formularios en React, tenemos dos tipos de componentes: los componentes controlados y los componentes no controlados.
Componentes controlados: son aquellos que tienen un estado que controla el valor del componente. Por lo tanto, el valor del componente se actualiza cuando el estado cambia.
La ventaja de este tipo de componentes es que son más fáciles de testear porque no dependen de la interfaz. También nos permiten crear validaciones muy fácilmente. La desventaja es que son más complejos de crear y mantener. Además, pueden tener un peor rendimiento, ya que provocan un re-renderizado cada vez que cambia el valor del input.
Componentes no controlados: son aquellos que no tienen un estado que controle el valor del componente. El estado del componente lo controla el navegador de forma interna. Para conocer el valor del componente, tenemos que leer el valor del DOM.
La ventaja de este tipo de componentes es que se crean de forma muy fácil y no tienes que mantener un estado. Además, el rendimiento es mejor, ya que no tiene que re-renderizarse al cambiar el valor del input. Lo malo es que hay que tratar más con el DOM directamente y crear código imperativo.
// 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⬆ Volver a índice
Los High Order Components son funciones que reciben un componente como parámetro y devuelven un componente.
function withLayout ( Component ) {
return function ( props ) {
return < main >
< section >
< Component { ... props } />
</ section >
</ main >
}
} En este caso, la función withLayout recibe un componente como parámetro y devuelve un componente. El componente devuelto renderiza el componente que se le pasa como parámetro dentro de un layout.
Es un patrón que nos permite reutilizar código y así podemos inyectar funcionalidad, estilos o cualquier otra cosa a un componente de forma sencilla.
Con la llegada de los hooks, los HOCs se han vuelto menos populares, pero todavía se usan en algunos casos.
⬆ Volver a índice
Son un patrón de diseño de React que nos permite reutilizar código entre componentes e inyectar información en el renderizado de los componentes.
< DataProvider render = { data => (
< h1 > Hello { data . target } </ h1 >
) } /> En este caso, el componente DataProvider recibe una función render como prop. Ahí le indicamos qué es lo que debe renderizar usando la información que recibe como parámetro.
La implementación del DataProvider con funciones podría ser la siguiente:
function DataProvider ( { render } ) {
const data = { target : 'world' }
return render ( data )
} También se puede encontrar este patrón usando la prop children en los componentes.
< DataProvider >
{ data => (
< h1 > Hello { data . target } </ h1 >
) }
</ DataProvider >Y la implementación sería similar:
function DataProvider ( { children } ) {
const data = { target : 'world' }
return children ( data )
} Este patrón es usado por grandes bibliotecas como react-router , formik o react-motion .
⬆ Volver a índice
if en el renderizado de un componente? En React, no podemos usar un if en el renderizado de un componente porque no es una expresión válida de JavaScript, es una declaración. Las expresiones son aquellas que devuelven un valor y las declaraciones no devuelven ningún valor.
En JSX solo podemos usar expresiones, por eso usamos ternarias, que sí son expresiones.
// 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 >
)
} De la misma forma, tampoco podemos usar for , while o switch dentro del renderizado de un componente.
⬆ Volver a índice
A la hora de actualizar el estado de React, debemos utilizar la función que nos facilita el hook useState para actualizar el estado.
const [ count , setCount ] = useState ( 0 )
setCount ( count + 1 )¿Por qué es esto necesario? En primer lugar, el estado en React debe ser inmutable. Es decir, no podemos modificar el estado directamente, sino que debemos siempre crear un nuevo valor para el nuevo estado.
Esto nos permite que la integridad de la UI respecto a los datos que renderiza siempre es correcta.
Por otro lado, llamar a una función le permite a React saber que el estado ha cambiado y que debe re-renderizar el componente si es necesario. Además esto lo hace de forma asíncrona, por lo que podemos llamar a setCount tantas veces como queramos y React se encargará de actualizar el estado cuando lo considere oportuno.
⬆ Volver a índice
En los componentes de clase, el ciclo de vida de un componente se divide en tres fases:
Dentro de este ciclo de vida, existe un conjunto de métodos que se ejecutan en el componente.
Estos métodos se definen en la clase y se ejecutan en el orden que se muestran a continuación:
En cada uno de estos métodos podemos ejecutar código que nos permita controlar el comportamiento de nuestro componente.
⬆ Volver a índice
index como key en un listado de React?Cuando renderizamos una lista de elementos, React necesita saber qué elementos han cambiado, han sido añadidos o eliminados.
Para ello, React necesita una key única para cada elemento de la lista. Si no le pasamos una key, React usa el índice del elemento como key.
const List = ( ) => {
const [ items , setItems ] = useState ( [ 'Item 1' , 'Item 2' , 'Item 3' ] )
return (
< ul >
{ items . map ( ( item , index ) => (
< li key = { index } > { item } </ li >
) ) }
</ ul >
)
} En este caso, React usa el índice del elemento como key . Esto puede ser un problema si la lista se reordena o se eliminan elementos del array, ya que el índice de los elementos cambia.
En este caso, React no sabe qué elementos han cambiado y puede que se produzcan errores.
Un ejemplo donde se ve el problema:
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 >
)
}⬆ Volver a índice
useMemo ? El hook useMemo es un hook que nos permite memorizar el resultado de una función. Esto quiere decir que si la función que le pasamos como parámetro no ha cambiado, no se ejecuta de nuevo y se devuelve el resultado que ya se había calculado.
import { useMemo } from 'react'
function Counter ( { count } ) {
const double = useMemo ( ( ) => count * 2 , [ count ] )
return (
< div >
< p > Contador: { count } </ p >
< p > Doble: { double } </ p >
</ div >
)
} En este caso, el componente Counter recibe una prop count que es un número. El componente calcula el doble de ese número y lo muestra en pantalla.
El hook useMemo recibe dos parámetros: una función y un array de dependencias. La función se ejecuta cuando el componente se renderiza por primera vez y cuando alguna de las dependencias cambia, en este ejemplo la prop count .
La ventaja es que si la prop count no cambia, se evita el cálculo del doble y se devuelve el valor que ya se había calculado previamente.
⬆ Volver a índice
useMemo para optimizar nuestros componentes? No. useMemo es una herramienta que nos permite optimizar nuestros componentes, pero no es una herramienta mágica que nos va a hacer que nuestros componentes sean más rápidos. A veces el cálculo de un valor es tan rápido que no merece la pena memorizarlo. Incluso, en algunos casos, puede ser más lento memorizarlo que calcularlo de nuevo.
⬆ Volver a índice
useCallback ? El hook useCallback es un hook que nos permite memorizar una función. Esto quiere decir que si la función que le pasamos como parámetro no ha cambiado, no se ejecuta de nuevo y se devuelve la función que ya se había calculado.
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 >
)
} En este caso, el componente Counter recibe una prop count que es un número y una prop onIncrement que es una función que se ejecuta cuando se pulsa el botón.
El hook useCallback recibe dos parámetros: una función y un array de dependencias. La función se ejecuta cuando el componente se renderiza por primera vez y cuando alguna de las dependencias cambia, en este ejemplo la prop count o la prop onIncrement .
La ventaja es que si la prop count o la prop onIncrement no cambian, se evita la creación de una nueva función y se devuelve la función que ya se había calculado previamente.
⬆ Volver a índice
useCallback para optimizar nuestros componentes? No. useCallback es una herramienta que nos permite optimizar nuestros componentes, pero no es una herramienta mágica que nos va a hacer que nuestros componentes sean más rápidos. A veces la creación de una función es tan rápida que no merece la pena memorizarla. Incluso, en algunos casos, puede ser más lento memorizarla que crearla de nuevo.
⬆ Volver a índice
useCallback y useMemo ? La diferencia entre useCallback y useMemo es que useCallback memoriza una función y useMemo memoriza el resultado de una función.
En cualquier caso, en realidad, useCallback es una versión especializada de useMemo . De hecho se puede simular la funcionalidad de useCallback con useMemo :
const memoizedCallback = useMemo ( ( ) => {
return ( ) => {
doSomething ( a , b )
}
} , [ a , b ] )⬆ Volver a índice
Las refs nos permiten crear una referencia a un elemento del DOM oa un valor que se mantendrá entre renderizados. Se pueden declarar por medio del comando createRef o con el hook useRef .
⬆ Volver a índice
useRef ? En el siguiente ejemplo vamos a guardar la referencia en el DOM a un elemento <input> y vamos a cambiar el foco a ese elemento cuando hacemos clic en el botón.
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 >
</ >
)
} 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.
⬆ Volver a índice
useLayoutEffect ? useLayoutEffect funciona igual que el hook useEffect , con la excepción de que este se dispara sincrónicamente después de leer todas las mutaciones del DOM.
Llama useLayoutEffect en el nivel superior del componente.
import { useLayoutEffect } from 'react' ;
useLayoutEffect ( ( ) => {
return ( ) => {
}
} , [ ] ) ; useLayoutEffect recibe dos argumentos:
Aunque el useEffect es el hook de renderizado más usado, si se necesita que los efectos del DOM muten cambiando la apariencia entre el efecto y el renderizado, entonces es conveniente que uses el useLayoutEffect .
useLayoutEffect El orden de ejecución del useLayoutEffect , ya que se ejecuta de forma síncrona, al momento en que React termina de ejecutar todas las mutaciones, pero antes de renderizarlo en pantalla, es el siguiente:
⬆ Volver a índice
Los componentes stateless son componentes que no tienen estado. Estos componentes se crean con una function y no tienen acceso al estado de la aplicación. La ventaja que tienen estos componentes es que hace que sea más fácil crear componentes puros (que siempre renderizan lo mismo para unas mismas props).
// Este es un ejemplo de componente stateless
function Button ( { text } ) {
return (
< button >
{ text }
</ button >
)
}⬆ Volver a índice
Para prevenir el comportamiento por defecto de un evento en React, debemos usar el método preventDefault :
function Form ( { onSubmit } ) {
const handleSubmit = ( event ) => {
event . preventDefault ( )
onSubmit ( )
}
return < form onSubmit = { handleSubmit } >
< input type = "text" />
< button type = "submit" > Enviar </ button >
</ form >
}⬆ Volver a índice
StrictMode en React? El StrictMode es un componente que nos permite activar algunas comprobaciones de desarrollo en React. Por ejemplo, detecta componentes que se renderizan de forma innecesaria o funcionalidades obsoletas que se están usando.
import { StrictMode } from 'react'
function App ( ) {
return (
< StrictMode >
< Component />
</ StrictMode >
)
}⬆ Volver a índice
Los componentes de React se pueden exportar de dos formas:
Para exportar un componente por defecto, usamos la palabra reservada default :
// button.jsx
export default function Button ( ) {
return < button > Click </ button >
}
// App.jsx
import Button from './button.jsx'
function App ( ) {
return < Button />
}La gran desventaja que tiene la exportación por defecto es que a la hora de importarlo puedes usar el nombre que quieras. Y esto trae problemas, ya que puedes no usar siempre el mismo en el proyecto o usar un nombre que no sea correcto con lo que importas.
// 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 />
}Los exports nombrados nos obligan a usar el mismo nombre en todos los archivos y, por tanto, nos aseguramos de que siempre estamos usando el nombre correcto.
// button.jsx
export function Button ( ) {
return < button > Click </ button >
}
// App.jsx
import { Button } from './button.jsx'
function App ( ) {
return < Button />
}⬆ Volver a índice
Para exportar múltiples componentes de un mismo archivo, podemos usar la exportación nombrada:
// button.jsx
export function Button ( { children } ) {
return < button > { children } </ button >
}
export function ButtonSecondary ( { children } ) {
return < button class = "btn-secondary" > { children } </ button >
}⬆ Volver a índice
Para importar de forma dinámica un componente en React debemos usar la función import() , el método lazy() de React y el componente 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 >
}Vamos a ver en detalle cada uno de los elementos que hemos usado:
La función import() es parte del estándar de ECMAScript y nos permite importar de forma dinámica un módulo. Esta función devuelve una promesa que se resuelve con el módulo importado.
El método lazy() de React nos permite crear un componente que se renderiza de forma diferida. Este método recibe una función que debe devolver una promesa que se resuelve con un componente. En este caso, se resolverá con el componente que tenemos en el fichero button.jsx . Ten en cuenta que el componente que devuelve lazy() debe ser un componente de React y ser exportado por defecto ( export default ).
El componente Suspense nos permite mostrar un mensaje mientras se está cargando el componente. Este componente recibe una prop fallback que es el mensaje que se muestra mientras se está cargando el componente.
⬆ Volver a índice
En React, nuestras aplicaciones están creadas a partir de componentes. Estos componentes se pueden importar de forma estática o dinámica .
La importación de componentes de forma estática es la forma más común de importar componentes en React. En este caso, los componentes se importan en la parte superior del fichero y se renderizan en el código. El problema es que, si siempre lo hacemos así, es bastante probable que estemos cargando componentes que no se van a usar desde el principio.
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 >
)
} Este componente SuperBigModal se importa de forma estática, por lo que se carga desde el principio. Pero, ¿qué pasa si el usuario no da click en el botón para mostrar la modal? En este caso, está cargando el componente pese a que no lo está usando.
Si queremos ofrecer la mejor experiencia a nuestros usuarios, debemos intentar que la aplicación cargue lo más rápido posible. Por eso, es recomendable importar de forma dinámica los componentes que no se van a usar desde el principio.
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 >
)
} De esta forma, la parte de código que importa el componente SuperBigModal se carga de forma dinámica, es decir, cuando el usuario da click en el botón para mostrar la modal.
Dependiendo del empaquetador de aplicaciones web que uses y su configuración, es posible que el resultado de la carga sea diferente (algunos creará un archivo a parte del bundle principal, otros podrían hacer un streaming del HTML...) pero la intención del código es la misma.
Así que siempre debemos intentar cargar los componentes de forma dinámica cuando no se vayan a usar desde el principio, sobretodo cuando están detrás de la interacción de un usuario. Lo mismo podría ocurrir con rutas completas de nuestra aplicación. ¿Por qué cargar la página de About si el usuario está visitando la página principal?
⬆ Volver a índice
No, no es necesario que los componentes se exporten por defecto para poder cargarlos de forma dinámica. Podemos exportarlos de forma nombrada y cargarlos de forma dinámica... pero no es lo más recomendable ya que el código necesario es mucho más lioso.
// 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 >
)
}Otra opción es tener un fichero intermedio que exporte el componente de forma por defecto y que sea el que importemos de forma dinámica.
// 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 >
)
}⬆ Volver a índice
El contexto es una forma de pasar datos a través de la jerarquía de componentes sin tener que pasar props manualmente en cada nivel.
Para crear un contexto en React usamos el hook createContext :
import { createContext } from 'react'
const ThemeContext = createContext ( ) Para usar el contexto, debemos envolver el árbol de componentes con el componente Provider :
< ThemeContext . Provider value = "dark" >
< App />
</ ThemeContext . Provider > Para consumir el contexto, debemos usar el hook useContext :
import { useContext } from 'react'
function Button ( ) {
const theme = useContext ( ThemeContext )
return < button className = { theme } > Haz clic aquí </ button >
}⬆ Volver a índice
SyntheticEvent en React? El SyntheticEvent es una abstracción del evento nativo del navegador. Esto le permite a React tener un comportamiento consistente en todos los navegadores.
Dentro del SyntheticEvent puede encontrarse una referencia al evento nativo en su atributo nativeEvent
function App ( ) {
function handleClick ( event ) {
console . log ( event )
}
return < button onClick = { handleClick } > Haz clic aquí </ button >
}⬆ Volver a índice
flushSync en React? flushSync(callback) Obliga a React a ejecutar de manera síncrona todas las actualizaciones de los state dentro del callback proporcionado. Así se asegura que el DOM se actualiza inmediatamente.
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 >
} NOTA: flushSync puede afectar significativamente el rendimiento. Úsalo con moderación.
⬆ Volver a índice
Los Error Boundaries son componentes que nos permiten manejar los errores que se producen en el árbol de componentes. Para crear un Error Boundary, debemos crear un componente que implemente el método 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
}
}De esta forma podemos capturar los errores que se producen en el árbol de componentes y mostrar un mensaje de error personalizado mientras evitamos que nuestra aplicación se rompa completamente.
Ahora podemos envolver el árbol de componentes con el componente ErrorBoundary :
< ErrorBoundary >
< App />
</ ErrorBoundary >Podemos crear un Error Boundary en cualquier nivel del árbol de componentes, de esta forma podemos tener un control más granular de los errores.
< ErrorBoundary >
< App />
< ErrorBoundary >
< SpecificComponent />
</ ErrorBoundary >
</ ErrorBoundary >Por ahora no existe una forma nativa de crear un Error Boundary en una función de React. Para crear un Error Boundary en una función, puedes usar la librería react-error-boundary.
⬆ Volver a índice
El reenvío de referencia o Forward Refs es una técnica que nos permite acceder a una referencia de un componente hijo desde un componente padre.
// 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 >
)
} En este ejemplo, recuperamos la referencia del botón (elemento HTML <button> ) y la recupera el componente padre ( Parent ), para poder hacer focus en él gracias al uso de forwardRef en el componente hijo ( Button ).
Para la gran mayoría de componentes esto no es necesario pero puede ser útil para sistemas de diseño o componentes de terceros reutilizables.
⬆ Volver a índice
React proporciona una forma de validar el tipo de las props de un componente en tiempo de ejecución y en modo desarrollo. Esto es útil para asegurarnos de que los componentes se están utilizando correctamente.
El paquete se llama prop-types y se puede instalar con npm install prop-types .
import PropTypes from "prop-types"
function App ( props ) {
return < h1 > { props . title } </ h1 >
}
App . propTypes = {
title : PropTypes . string . isRequired ,
} En este ejemplo, estamos validando que la prop title sea de tipo string y que sea obligatoria.
Existen una colección de PropTypes ya definidas para ayudarte a comprobar los tipos de las props más comunes:
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 A todas estas se le puede añadir la propiedad isRequired para indicar que es obligatoria.
Otra opción es usar TypeScript, un lenguaje de programación que compila a JavaScript y que ofrece validación de tipos de forma estática. Ten en cuenta que mientras que TypeScript comprueba los tipos en tiempo de compilación, las PropTypes lo hacen en tiempo de ejecución.
⬆ Volver a índice
Para validar las propiedades de un objeto que se pasa como prop, podemos usar la propiedad shape de 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 ,
} ) ,
}⬆ Volver a índice
Para validar las propiedades de un array que se pasa como prop, podemos usar la propiedad arrayOf de PropTypes :
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 ,
} En este caso estamos validando que items sea un array y que cada uno de sus elementos sea un objeto con la propiedad text de tipo string . Además, la prop es obligatoria.
⬆ Volver a índice
Una de las razones por las que se creó React es para evitar los ataques XSS ( Cross-Site Scripting ), impidiendo que un usuario pueda inyectar código HTML en la página.
Por ello, React al intentar evaluar un string que contiene HTML lo escapa automáticamente. Por ejemplo, si intentamos renderizar el siguiente string:
const html = "<h1>My title</h1>"
function App ( ) {
return < div > { html } </ div >
}Veremos que en lugar de renderizar el HTML, lo escapa:
< 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.
⬆ Volver a índice
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.⬆ Volver a índice
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 ;⬆ Volver a índice
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.
⬆ Volver a índice
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.
⬆ Volver a índice
cloneElement de React?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.
⬆ Volver a índice
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.
⬆ Volver a índice
StrictMode renderiza dos veces la aplicación? 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 .
⬆ Volver a índice
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.
⬆ Volver a índice
useEffect en React? 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.
⬆ Volver a índice
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.
⬆ Volver a índice
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.
⬆ Volver a índice
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.
⬆ Volver a índice
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 >
}⬆ Volver a índice
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 >
}⬆ Volver a índice
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 )
} ,
}
)⬆ Volver a índice
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() .
⬆ Volver a índice
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 >
)
}⬆ Volver a índice
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' } ,
}
)
}⬆ Volver a índice
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 ( )
} )⬆ Volver a índice
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 )
} )⬆ Volver a índice
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.
⬆ Volver a índice
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:
¿Qué es el renderizado de listas en React?
¿Por qué puede ser mala práctica usar el ´index´ como key en un listado de React?
⬆ Volver a índice
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:
⬆ Volver a índice
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.
⬆ Volver a índice
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:
⬆ Volver a índice
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.
⬆ Volver a índice
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
⬆ Volver a índice