ゼロからエキスパートへ。スペイン語
プロジェクトが気に入ったら、そのまま
Twitchプログラミングストリーム:twitch.tv/midudev
Discord開発コミュニティ:discord.gg/midudev
の子供たちとは何ですか、そしてそれは何のためにありますか?
クラスを使用できないのですか?
使用状態フックは何をしますか?
useEffect フックは何をしますか?
useEffect フックのユースケースについて説明します。
効果
useIdフックは何をしますか?
useEffectを持つことができますか?
効果
useLayoutTEffect の違いは何ですか?
ifを使用できないのはなぜですか?
インデックスをReactリストのキーとして使用するのはなぜ不正行為なのでしょうか?
useMemoフックは何のためにありますか?
useMemoを使用するのは良い考えですか?
useCallback フックは何のためにありますか?
useCallbackを使用するのは良い考えですか?
useCallback と useMemo の違いは何ですか?
useRefフックはどのように機能しますか?
useLayoutEffectフックは何をしますか?
StrictModeとは何ですか?
合成イベントとは何ですか?
フラッシュシンクとは何ですか?
useImperativeHandleフックは何のためにありますか?
React cloneElement メソッドとは何ですか?
StrictMode がアプリケーションを 2 回レンダリングするのはなぜですか?
useEffectを使用してフェッチ請願を中止するにはどうすればよいですか?
useDebugValue フックとは何ですか?
プロファイラーとは何ですか?
renderToStaticNodeStream() と renderToPipeableStream() の違いは何ですか?
遅延値フックは何のために使用されますか?
renderToReadableStream() メソッドは何のためにありますか?
Reactは、ユーザーインターフェイスを構築するためのオープンソースのJavaScriptライブラリです。これはUIのコンポーネント化に基づいています:インターフェイスは、独自の状態を含む独立したコンポーネントに分割されます。コンポーネントの状態が変更されると、React はインターフェイスを再レンダリングします。
これにより、Reactは、インターフェイスをより小さく再利用可能な部分に分割できるため、複雑なインターフェイスを構築するための非常に便利なツールになります。
これは、Facebookで働いていたソフトウェアエンジニアであり、複雑なユーザーインターフェイスを作成する方法を簡素化したいと考えていたジョーダンウォークによって2011年に作成されました。
非常に人気のあるライブラリであり、フェイスブック、ネットフリックス、Airbnb、ツイッター、インスタグラムなどの多くの企業で使用されています。
興味のあるリンク:
Reactの主な機能は次のとおりです。
コンポーネント: React は UI のコンポーネント化に基づいています。インターフェイスは、独自の状態を含む独立したコンポーネントに分割されます。コンポーネントの状態が変更されると、React はインターフェイスを再レンダリングします。
仮想 DOM: React は仮想 DOM を使用してコンポーネントをレンダリングします。仮想 DOM は、実際の DOM のメモリ内表現です。コンポーネントの状態が変更されると、React はインターフェイスを再レンダリングします。実際のDOMを変更する代わりに、Reactは仮想DOMを変更し、仮想DOMを実際のDOMと比較します。このようにして、Reactは実際のDOMにどのような変更を適用する必要があるかを認識します。
宣言型:Reactは宣言型であり、タスクの実行方法を指定するのではなく、何を実行するかを指定します。これにより、コードの理解と保守が容易になります。
単方向: React は一方向であり、データは一方向にのみ流れます。データは親コンポーネントから子コンポーネントに流れます。
ユニバーサル: React はクライアントとサーバーの両方で実行できます。また、React Nativeを使用して、AndroidおよびiOS用のネイティブアプリを作成することもできます。
指示に基づいてインターフェイスをレンダリングする方法は説明しません。何をレンダリングするかを教え、Reactがレンダリングを処理します。
宣言型と命令型の例:
// Declarativo
const element = <h1>Hello, world</h1>
// Imperativo
const element = document.createElement('h1')
element.innerHTML = 'Hello, world'
コンポーネントは、インターフェイスの一部をレンダリングするコードです。コンポーネントは、パラメーター化、再利用でき、独自の状態を含めることができます。
React では、コンポーネントは関数またはクラスを使用して作成されます。
Reactは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>
}
どちらのコードも同等です。
JSXは、トランスパイラーまたはコンパイラを使用して、ブラウザーで互換性のあるJavaScriptコードに変換されます。今日最も有名なのはBabelで、変換をサポートするために多くのプラグインを使用していますが、SWCなどの他のプラグインもあります。
JSXがどのようにバベルコードの遊び場に変わるかを見ることができます。
トランスパイラーが不要な特別な場合があります。たとえば、DenoはJSX構文をネイティブにサポートしており、互換性を持たせるためにコードを変換する必要はありません。
コンポーネントは、小道具を受け取り、要素を返す関数またはクラスです。 要素は、DOM ノードまたは React コンポーネントのインスタンスを表すオブジェクトです。
// Elemento que representa un nodo del DOM
{
type: 'button',
props: {
className: 'button button-blue',
children: {
type: 'b',
props: {
children: 'OK!'
}
}
}
}
// Elemento que representa una instancia de un componente
{
type: Button,
props: {
color: 'blue',
children: 'OK!'
}
}
React のコンポーネントは、React 要素を返す関数またはクラスです。現在、最も推奨されるのは関数を使用することです。
function HelloWorld() {
return <h1>Hello World!</h1>
}
ただし、クラスを使用してReactコンポーネントを作成することもできます。
import { Component } from 'react'
class HelloWorld extends Component {
render() {
return <h1>Hello World!</h1>
}
}
重要なことは、関数またはクラスの名前が大文字で始まることです。これは、ReactがコンポーネントとHTML要素を区別できるようにするために必要です。
小道具はコンポーネントのプロパティです。これは、あるコンポーネントから別のコンポーネントに渡されるデータです。たとえば、ボタンを表示するコンポーネントがある場合、それに prop を渡して、ボタンにそのテキストを表示させることができます。
Button
text
function Button(props) {
return <button>{props.text}</button>
}
コンポーネントは汎用ボタンであり、prop はボタンに表示されるテキストであることを理解できました。そのため、再利用可能なコンポーネントを作成しています。
Button
text
また、JSX内でJavaScript式を使用する場合は、それらをでラップする必要があり、この場合はオブジェクト、そうしないとJSXはそれをプレーンテキストと見なします。
{}
props
これを使用するには、コンポーネントの名前を示し、必要な小道具を渡します。
<Button text="Haz clic aquí" />
<Button text="Seguir a @midudev" />
小道具は、関数と同じようにコンポーネントをパラメータ化する方法です。他のコンポーネントも含め、あらゆるタイプのデータをコンポーネントに渡すことができます。
children
prop は、コンポーネントに渡される特別な prop です。これは、コンポーネントがラップする要素を含むオブジェクトです。
children
たとえば、タイトルとコンテンツを含むカードを表示するコンポーネントがある場合、prop を使用してコンテンツを表示できます。
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>
この場合、prop には要素 .
children
<p>Contenido de la tarjeta</p>
Reactで再利用可能なコンポーネントを作成するには、propの使用方法を知り、知ることが非常に重要です。
children
Props は、親コンポーネントから子コンポーネントに引数として渡されるオブジェクトです。これらは不変であり、子コンポーネントから変更することはできません。
状態は、コンポーネント内で定義される値です。その値は不変です(直接変更することはできません)が、Reactがコンポーネントを再レンダリングするために新しい状態値を設定できます。
したがって、小道具と状態の両方がコンポーネントのレンダリングに影響しますが、それらの管理は異なります。
はい、小道具の値で状態を初期化できます。ただし、小道具が変更されても、ステータスは自動的に更新されないことに注意してください。これは、コンポーネントが最初にマウントされたときに状態が一度初期化されるためです。
たとえば、機能コンポーネントの場合:
const Counter = () => {
const [count, setCount] = useState(0)
return (
<div>
<Count count={count} />
<button onClick={() => setCount(count + 1)}>Aumentar</button>
</div>
)
}
const Count = ({ count }) => {
const [number, setNumber] = useState(count)
return <p>{number}</p>
}
この場合、コンポーネントはその状態を prop の値で初期化します。ただし、propの値を変更しても、ステータスは自動的に更新されません。したがって、クリックすると、画面に常に数字の0が表示されます。
Count
count
count
この例では、コンポーネントで prop を使用するだけで、常に再レンダリングされます。
count
Count
コンポーネントの状態をできるだけ避け、可能な限り、小道具から表示される値を計算することをお勧めします。
小道具で状態を初期化する必要がある場合は、小道具に接頭辞を追加して、それが状態の初期値であることを示すことをお勧めします。
initial
const Input = ({ initialValue }) => {
const [value, setValue] = useState(initialValue)
return (
<input
value={value}
onChange={e => setValue(e.target.value)}
/>
)
}
小道具がステータスを更新すると考えるのは非常によくある間違いなので、それを覚えておいてください。
条件付きレンダリングは、条件に応じて1つのコンポーネントまたは別のコンポーネントを表示する方法です。
Reactで条件付きレンダリングを行うには、三項演算子を使用します。
function Button({ text }) {
return text
? <button>{text}</button>
: null
}
この場合、prop が存在する場合は、ボタンがレンダリングされます。存在しない場合は、何もレンダリングされません。
text
演算子 を使用した条件付きレンダリングの実装を見つけるのが一般的です。
&&
function List({ listArray }) {
return listArray?.length && listArray.map(item=>item)
}
それは理にかなっているようです...が正(ゼロより大きい)の場合、マップを描画します。!いやそうではありません!
length
length
三項演算子を用いることが好ましい。ケントC.ドッズはそれについて話している興味深い記事を持っています。JSXでは&&ではなく三項を使用する
class
ReactのコンポーネントにCSSクラスを適用するには、 prop を使用します。
className
function Button({ text }) {
return (
<button className="button">
{text}
</button>
)
}
それが呼ばれる理由は、それがJavaScriptの予約語だからです。そのため、JSXでは、CSSクラスを適用するためにそれを使用する必要があります。
className
class
className
ReactのコンポーネントにインラインCSSスタイルを適用するには、小道具を使用します。HTMLで行う方法の違いは、Reactではスタイルがテキスト文字列ではなくオブジェクトとして渡されることです(これは、二重括弧でより明確に確認できます。
style
function Button({ text }) {
return (
<button style={{ color: 'red', borderRadius: '2px' }}>
{text}
</button>
)
}
CSS プロパティ名もキャメルケースであることに注意してください。
Reactでコンポーネントを条件付きでスタイル設定するには、小道具と三項演算子を使用します。
style
function Button({ text, primary }) {
return (
<button style={{ color: primary ? 'red' : 'blue' }}>
{text}
</button>
)
}
前のケースでは、小道具が の場合、ボタンの色は赤になります。そうでない場合は、青色になります。
primary
true
クラスを使用して同じメカニズムに従うこともできます。この場合、三項演算子を使用して、クラスを追加するかどうかを決定します。
function Button({ text, primary }) {
return (
<button className={primary ? 'button-primary' : ''}>
{text}
</button>
)
}
次のようなライブラリを使用することもできます。
classnames
import classnames from 'classnames'
function Button({ text, primary }) {
return (
<button className={classnames('button', { primary })}>
{text}
</button>
)
}
この場合、prop が の場合、クラスがボタンに追加されます。そうでない場合は、追加されません。一方、クラスは常に追加されます。
primary
true
primary
button
リストレンダリングは、要素の配列を反復処理し、各要素のReact要素をレンダリングする方法です。
Reactでリストをレンダリングするには、配列メソッドを使用します。
map
function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
)
}
この場合、項目のリストは を使用してレンダリングされます。コンポーネントは、 型のオブジェクトの配列である prop を受け取ります。コンポーネントは、配列内の要素ごとに 1 つの要素をレンダリングします。
List
List
items
[{id:1, name: "John", id:1, name: "Doe"}]
List
li
要素には、各要素の一意の識別子である小道具があります。これは、Reactがリスト内の各アイテムを識別して効率的に更新できるようにするために必要です。これに関するより詳細な説明を以下に示します。
li
key
コンポーネントのレンダリングの外部でコメントを書き込む場合は、JavaScript のコメント構文をシームレスに使用できます。
function Button({ text }) {
// Esto es un comentario
/* Esto es un comentario
de varias líneas */
return (
<button>
{text}
</button>
)
}
コンポーネントのレンダリング内でコメントを書き込む場合は、コメントを中括弧で囲み、常にブロックコメント構文を使用する必要があります。
function Button({ text }) {
return (
<button>
{/* Esto es un comentario en el render */}
{text}
</button>
)
}
Reactのコンポーネントにイベントを追加するには、camelCaseのネイティブブラウザイベント構文と名前を使用します。
on
function Button({ text, onClick }) {
return (
<button onClick={onClick}>
{text}
</button>
)
}
この場合、コンポーネントは関数である prop を受け取ります。ユーザーがボタンをクリックすると、関数が実行されます。
Button
onClick
onClick
Reactでイベントを処理する関数にパラメータを渡すには、匿名関数を使用できます。
function Button({ id, text, onClick }) {
return (
<button onClick={() => onClick(id)}>
{text}
</button>
)
}
ユーザーがボタンをクリックすると、prop の値をパラメーターとして渡すことで関数が実行されます。
onClick
id
prop の値を渡すことで関数を実行する関数を作成することもできます。
onClick
id
function Button({ id, text, onClick }) {
const handleClick = (event) => { // handleClick recibe el evento original
onClick(id)
}
return (
<button onClick={handleClick}>
{text}
</button>
)
}
状態は、時間の経過と共に変化する可能性のあるデータを含むオブジェクトです。React では、状態はインターフェイスへの変更を制御するために使用されます。
概念を理解するために、ルームスイッチについて考えてください。これらのスイッチには通常、オンとオフの2つの状態があります。スイッチを入れて装着するとライトが点灯し、ライトを点灯させると消灯します。
on
off
これと同じ概念をユーザー インターフェイスに適用できます。たとえば、Facebook の [いいね] ボタンには、ユーザーが「いいね!」した時と「いいね!」しなかったときのステータスが表示されます。
meGusta
true
false
状態にブール値を含めることができるだけでなく、オブジェクト、配列、数値などを持つこともできます。
たとえば、カウンタを表示するコンポーネントがある場合、ステータスを使用してカウンタの値を制御できます。
Counter
Reactでステータスを作成するには、フックを使用します。
useState
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>Contador: {count}</p>
<button onClick={() => setCount(count + 1)}>Aumentar</button>
</div>
)
}
フックを使用すると、次の 2 つの位置のいずれかが返されます。
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>
)
}
}
フックは、関数で作成されたコンポーネントに状態やその他のReact機能を持たせることを可能にするReact APIです。
以前はこれは不可能であり、ライブラリのすべての可能性にアクセスできるようにコンポーネントを作成する必要がありました。
class
フックはフックであり、正確には、機能コンポーネントをReactが提供するすべての機能にフックできるということです。
useState
フックは状態変数を作成するために使用され、その値が動的であり、時間の経過とともに変化する可能性があり、使用されているコンポーネントの再レンダリングが必要であることを意味します。
useState
パラメータを受け取る:
次の 2 つの変数を持つ配列を返します。
setIsOpen(isOpen => !isOpen)
この例では、の値が 0 で初期化され、button イベントの関数で値が変更されるたびにレンダリングされる方法を示します。
count
setCount
onClick
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return (
<>
<p>Contador: {count}</p>
<button onClick={() => setCount(count => count + 1)}>Aumentar</button>
</>
)
}
複数のコンポーネントが状態の同じデータを共有する必要がある場合は、その共有状態を最も近い共通の祖先に昇格させることをお勧めします。
要するにあの。2 つの子コンポーネントが親の同じデータを共有する場合は、子のローカル状態を維持するのではなく、状態を親に移動します。
それを理解するには、例を挙げて見るのが最善です。ウィッシュリストがあり、ギフトを追加して、リストにあるギフトの合計を表示できるようにしたいとします。
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
GiftList
TotalGifts
totalGifts
のステータスを親コンポーネントにアップロードする必要があり、ギフトの数を小道具としてコンポーネントに渡します。
gifts
App
TotalGifts
import { useState } from 'react'
export default function App () {
const [gifts, setGifts] = useState([])
const addGift = () => {
const newGift = prompt('¿Qué regalo quieres añadir?')
setGifts([...gifts, newGift])
}
return (
<>
<h1>Lista de regalos</h1>
<GiftList gifts={gifts} addGift={addGift} />
<TotalGifts totalGifts={gifts.length} />
</>
)
}
function GiftList ({ gifts, addGift }) {
return (
<>
<h2>Regalos</h2>
<ul>
{gifts.map(gift => (
<li key={gift}>{gift}</li>
))}
</ul>
<button onClick={addGift}>Añadir regalo</button>
</>
)
}
function TotalGifts ({ totalGifts }) {
return (
<>
<h2>Total de regalos</h2>
<p>{totalGifts}</p>
</>
)
}
これで、私たちがしたことは国家を高めることです。コンポーネントからコンポーネントに移動しました。これで、ギフトをコンポーネントにpropsとして渡し、ステータスを更新する方法もpropとしてコンポーネントに渡しました。
GiftList
App
GiftList
TotalGifts
useEffect
フックは、コンポーネントがレンダリングされるとき、またはエフェクトの依存関係が変更されたときにコードを実行するために使用されます。
useEffect
次の 2 つのパラメーターを受け取ります。
この例では、コンポーネントをロードするとき、および の値を変更するたびに、コンソールにメッセージを表示します。
count
import { useEffect, useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log('El contador se ha actualizado')
}, [count])
return (
<>
<p>Contador: {count}</p>
<button onClick={() => setCount(count + 1)}>Aumentar</button>
</>
)
}
useEffect
フックは、次のようなさまざまな方法で使用できます。
useEffect
resize
useEffect
その中で、ユーザーがウィンドウのサイズを変更したことを知るイベントなどのブラウザイベントをサブスクライブできます。メモリリークを回避するために、コンポーネントが分解されたときにサブスクライブを解除することが重要です。これを行うには、コンポーネントが逆アセンブルされたときに実行される関数を返す必要があります。
useEffect
resize
useEffect
import { useEffect } from 'react'
function Window() {
useEffect(() => {
const handleResize = () => {
console.log('La ventana se ha redimensionado')
}
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}, [])
return (
<p>Abre la consola y redimensiona la ventana</p>
)
}
useId
useIdは、HTML タグ属性に渡すことができる一意の識別子を生成するためのフックであり、アクセシビリティに特に役立ちます。
コンポーネントの最上位レベルで呼び出して、一意の ID を生成します。
useId
import { useId } from 'react'
function PasswordField() {
const passwordHintId = useId()
// ...
次に、生成された ID をさまざまな属性に渡します。
<>
<input type="password" aria-describedby={passwordHintId} />
<p id={passwordHintId}>
</>
タグを使用すると、2つのタグが相互に関連していることを指定することができ、画面に数回表示されても生成されたIDが衝突しないuseIdを使用して一意のIDを生成できます。
aria-describedby
PasswordField
完全な例は次のようになります。
import { useId } from 'react'
function PasswordField() {
const passwordHintId = useId()
return (
<>
<label>
Password:
<input
type="password"
aria-describedby={passwordHintId}
/>
</label>
<p id={passwordHintId}>
El password debe ser de 18 letras y contener caracteres especiales
</p>
</>
)
}
export default function App() {
return (
<>
<h2>Choose password</h2>
<PasswordField />
<h2>Confirm password</h2>
<PasswordField />
</>
)
}
ご覧のとおり、コンポーネントを2回使用しています。たとえば、IDを手元に置いた場合、IDは一意ではなく、複製されます。そのため、を使用してIDを自動的に生成することが重要です。
App
password
useId
コンポーネントがフックを使用してマウントされると、依存関係を渡さずにコードを実行できます。この場合、最初のパラメータとして渡された関数は、コンポーネントがマウントされたときに実行されます。
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>
)
}
フラグメントは、Reactではコンポーネント内の複数の要素を返すことができず、1つのルート要素のみを返すことができないため、DOMに余分な要素を追加せずに要素をグループ化する方法です。
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>
</>
)
}
複数の要素をラップするときに1つではなくFragmentを使用することが推奨される理由は次のとおりです。
div
div
div
div
div
div
display: block
これは、単一の目標を持つ親コンポーネントの作成に基づくコンポーネントデザインパターンであり、スムーズにレンダリングするために必要なプロパティを子に提供します。
新しいコンポーネントを構築するときに宣言構造を可能にし、その単純さと清潔さのためにコードを読むのにも役立ちます。
この設計の例としては、子要素をレンダリングするリストがあります。
<List>
<ListItem>Cat</ListItem>
<ListItem>Dog</ListItem>
</List>
const List = ({ children, ...props }) => (
<ul {...props} >
{children}
</ul>
);
const ListItem = ({ children, ...props }) => {
return (
<li {...props}>
{children}
</li>
);
};
export { List, ListItem };
これは簡単な例ですが、コンポーネントは必要に応じて複雑にすることができ、親と子の両方が独自の状態を持つことができます。
興味のあるリンク:
ジェナ・スミスによる化合物成分
複合成分 ケント・C・ドッズによるレッスン
Reactプロジェクトを最初から初期化する方法はいくつかあります。最も人気のあるものは次のとおりです。
npm create vite@latest my-app -- --template react
npx create-react-app my-app
今日最も人気があり推奨されるオプションはViteです。ソースnpmトレンド。
フレームワークの使用、最も人気のあるものは次のとおりです。
npx create-next-app@latest my-app
npm init gatsby
今日最も人気があり、推奨されるオプションはNextjsです。ソースnpmトレンド
それぞれがWebアプリケーションパッケージャーです。プロジェクトの依存関係を解決し、変更ごとに自動的に更新される開発環境を構築し、必要なすべての静的ファイルなどを使用してアプリケーションを本番用にパッケージ化する責任があります。
React DOM は、ブラウザーの React コンポーネントのレンダリングを担当するライブラリです。Reactは、さまざまな環境(モバイルデバイス、デスクトップアプリ、端末など)で使用できるライブラリであることに注意してください。
Reactライブラリは、乾燥するために、コンポーネント、フック、小道具のシステム、および状態の作成のエンジンです...React DOM は、特にブラウザで React コンポーネントをレンダリングするライブラリです。
たとえば、React Nativeも同じことを行いますが、モバイルデバイス用です。
Reactを学び、習得するには、JavaScriptを知っている必要があります。独自のDSL(ドメイン固有言語)に依存するAngularやVueなどの他のフレームワークやライブラリとは異なり、ReactはJSXと呼ばれるJavaScript構文拡張機能を使用します。後で詳しく説明しますが、最終的には、JavaScriptを少なく書くのはまだ構文上の砂糖です。
Reactでは、すべてがJavaScriptです。良くも悪くも。この本は、プログラミング言語の予備知識を当然のことと考えていますが、始める前に、知っておく必要のある最も重要な機能のいくつかを簡単にレビューします。
すでにJavaScriptをマスターしている場合は、この章をスキップして本を続けることができますが、参照のためにこの章をいつでも確認できることを忘れないでください。
EcmaScript モジュールは、異なるファイル間で変数、関数、およびクラスをインポートおよびエクスポートするための JavaScript のネイティブな方法です。今日では、特にWebpackのようなアプリケーションパッケージャーを使用する場合は、常にこの構文を使用します。
一方では、デフォルトでモジュールをエクスポートすることでモジュールを作成できます。
// sayHi.js
// exportamos por defecto el módulo sayHi
export default sayHi (message) {
console.log(message)
}
// index.js
// este módulo lo podremos importar con el nombre que queramos
import sayHi from './sayHi.js'
// al ser el módulo exportado por defecto podríamos usar otro nombre
import miduHi from './sayHi.js'
モジュールの名前付きエクスポートを作成して、モジュールに名前が割り当てられるようにし、モジュールをインポートするには、エクスポート時に使用された名前を正確に使用する必要があります。
// sayHi.js
// podemos usar exportaciones nombradas para mejorar esto
export const sayHi = (message) => console.log(message)
// y se pueden hacer tantas exportaciones de módulos nombrados como queramos
export const anotherHi = msg => alert(msg)
// index.js
// ahora para importar estos módulos en otro archivo podríamos hacerlo así
import {sayHi, anotherHi} from './sayHi.js'
これまでに見たインポートは、静的インポートとして知られています。これは、そのモジュールをインポートするファイルのアップロード時にそのモジュールがロードされることを意味します。
動的インポートもあるので、プログラムの実行時に、または決定したとき(たとえば、クリックに応答して)ロードされるモジュールをインポートできます。
document.querySelector('button').addEventListener('click', () => {
// los imports dinámicos devuelven una Promesa
import('./sayHi.js').then(module => {
// ahora podemos ejecutar el módulo que hemos cargado
module.default('Hola')
})
})
A> 動的インポートは、Webpack や Vite などのパッケージャーを操作する場合にも、一般的なバンドルの外部にロードされるチャンクを作成するため、便利です。目標は?アプリケーションのパフォーマンスを向上させます。
モジュールを操作するための構文は他にもありますが、私たちが見たものを知っていれば、本に従うのに十分でしょう。
重要である理由
まず、Reactは、インポートできるモジュールを通じてライブラリのさまざまな部分を提供します。さらに、コンポーネントはファイルに分割され、それぞれがESModulesを使用してインポートできます。
さらに、パフォーマンスの最適化の問題については、コンポーネントを動的にインポートできるため、ページを使用できるようにするためにロードする必要のある情報が少なくて済むため、ユーザーのエクスペリエンスが向上します。
三項は、構文を使用せずに条件を実行する方法です。これは、あまり多くのコードを書かないようにするためのショートカットの一種と言えます。
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')
重要である理由
グラフィカルインターフェイスでは、アプリケーションの状態やデータに応じて、画面に何らかのものをレンダリングしたいと思うのはごく普通のことです。これを行うには、JSX内ではるかに読みやすいため、三項を使用する代わりに使用されます。
if
矢印関数は、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
}
ただし、構文の変更に加えて、Reactで常に使用される矢印関数の他の特性があります。
// 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には、コンポーネントとフックという2つの非常に重要な概念があります。ここでは詳しく説明しませんが、重要なことは、両方が関数で構築されていることです。
引数が来ない場合にこれらの関数のパラメーターにデフォルト値を追加できることは、Reactを正常に制御できるようにするための鍵です。
たとえば、コンポーネントはパラメーターを受け取らない場合がありますが、それにもかかわらず、コンポーネントにデフォルトの動作を持たせる必要があります。あなたはこの方法でそれを得ることができます。
リテラル テンプレートまたは文字列テンプレートは、テキスト文字列に埋め込まれた式を許可することで、テキスト文字列を次のレベルに引き上げます。
const inicio = 'Hola'
const final = 'React'
// usando una concatenación normal sería
const mensaje = inicio + " " + final
// con los template literals podemos evaluar expresiones
const mensaje = `${inicio} ${final}`
ご覧のとおり、リテラルテンプレートを使用するには、記号 ''' を使用する必要があります。
さらに、複数行のテキスト文字列を使用することもできます。
重要である理由
Reactでは、これはさまざまなことに使用できます。インターフェイスに表示するテキスト文字列を作成するのは正常であるだけでなく...また、HTML 要素のクラスを動的に作成する場合にも役立ちます。リテラルテンプレートがいたるところにあることがわかります。
ECMAScript 2015 から、省略されたプロパティ名を使用してオブジェクトを開始できます。これは、キーと同じ名前の変数を値として使用する場合は、初期化を一度指定することができます。
const name = 'Miguel'
const age = 36
const book = 'React'
// antes haríamos esto
const persona = { name: name, age: age, book: book }
// ahora podemos hacer esto, sin repetir
const persona = { name, age, book }
重要である理由
Reactではオブジェクトを扱うことが多く、コードの保守と理解を容易にするために、常にできるだけ少ない行を書きたいと考えています。
分解構文は、さまざまな変数の配列またはオブジェクトプロパティから値を抽出できるJavaScript式です。
// antes
const array = [1, 2, 3]
const primerNumero = array[0]
const segundoNumero = array[1]
// ahora
const [primerNumero, segundoNumero] = array
// antes con objetos
const persona = { name: 'Miguel', age: 36, book: 'React' }
const name = persona.name
const age = persona.age
// ahora con objetos
const {age, name} = persona
// también podemos añadir valores por defecto
const {books = 2} = persona
console.log(persona.books) // -> 2
// también funciona en funciones
const getName = ({name}) => `El nombre es ${name}`
getName(persona)
重要である理由
Reactには、この構文を知っていて習得していることを前提とした基本的なコードがたくさんあります。オブジェクトと配列は、インターフェイスで表すデータを格納するのに最適なデータの種類と考えてください。ですから、それらを簡単に治療できることはあなたの人生をはるかに楽にするでしょう。
JavaScriptで配列を操作する方法を知ることは、あなたが習得することを考慮するための基本です。各メソッドは特定の操作を実行し、さまざまな種類のデータを返します。表示されるすべてのメソッドは、配列の各要素に対して実行されるコールバック(関数)を受け取ります。
最もよく使用される方法のいくつかを確認しましょう。
// tenemos este array con diferentes elementos
const networks = [
{
id: 'youtube',
url: 'https://midu.tube',
needsUpdate: true
},
{
id: 'twitter',
url: 'https://twitter.com/midudev',
needsUpdate: true
},
{
id: 'instagram',
url: 'https://instagram.com/midu.dev',
needsUpdate: false
}
]
// con .map podemos transformar cada elemento
// y devolver un nuevo array
networks.map(singleNetwork => singleNetwork.url)
// Resultado:
[
'https://midu.tube',
'https://twitter.com/midudev',
'https://instagram.com/midu.dev'
]
// con .filter podemos filtrar elementos de un array que no
// pasen una condición determinada por la función que se le pasa.
// Devuelve un nuevo array.
networks.filter(singleNetwork => singleNetwork.needsUpdate === true)
// Resultado:
[
{ id: 'youtube', url: 'https://midu.tube', needsUpdate: true },
{ id: 'twitter', url: 'https://twitter.com/midudev', needsUpdate: true }
]
// con .find podemos buscar un elemento de un array que
// cumpla la condición definida en el callback
networks.find(singleNetwork => singleNetwork.id === 'youtube')
// Resultado:
{ id: 'youtube', url: 'https://midu.tube', needsUpdate: true }
// con .some podemos revisar si algún elemento del array cumple una condición
networks.some(singleNetwork => singleNetwork.id === 'tiktok') // false
networks.some(singleNetwork => singleNetwork.id === 'instagram') // true
重要である理由
Reactでは、UIで表現する必要のあるデータを配列として保存するのがごく普通のことです。これは、多くの場合、それらを処理したり、フィルタリングしたり、情報を抽出したりする必要があることを意味します。これらの方法は最も使用されているため、少なくともこれらの方法を理解し、理解し、習得することが不可欠です。
スプレッド構文を使用すると、反復可能オブジェクトまたはオブジェクトを、その情報が期待される別の場所に展開できます。これを使用するには、直前に3つの省略記号を使用する必要があります。
...
const networks = ['Twitter', 'Twitch', 'Instagram']
const newNetwork = 'Tik Tok'
// creamos un nuevo array expandiendo el array networks y
// colocando al final el elemento newNetwork
// utilizando la sintaxis de spread
const allNetworks = [...networks, newNetwork]
console.log(allNetworks)
// -> [ 'Twitter', 'Twitch', 'Instagram', 'Tik Tok' ]
オブジェクトでも同じことを実現できるため、非常に簡単な方法で別のオブジェクトのすべてのプロパティを展開できます。
const midu = { name: 'Miguel', twitter: '@midudev' }
const miduWithNewInfo = {
...midu,
youtube: 'https://youtube.com/midudev',
books: ['Aprende React']
}
console.log(miduWithNewInfo)
// {
// name: 'Miguel',
// twitter: '@midudev',
// youtube: 'https://youtube.com/midudev',
// books: [ 'Aprende React' ]
// }
これはコピーを作成することに注意することが重要です、はい、しかし表面的なものです。オブジェクト内にオブジェクトがネストされている場合は、参照を変更できることを覚えておく必要があります。例を見てみましょう。
const midu = {
name: 'Miguel',
twitter: '@midudev',
experience: {
years: 18,
focus: 'javascript'
}
}
const miduWithNewInfo = {
...midu,
youtube: 'https://youtube.com/midudev',
books: ['Aprende React']
}
// cambiamos un par de propiedades de la "copia" del objeto
miduWithNewInfo.name = 'Miguel Ángel'
miduWithNewInfo.experience.years = 19
// hacemos un console.log del objeto inicial
console.log(midu)
// en la consola veremos que el nombre no se ha modificado
// en el objeto original pero los años de experiencia sí
// ya que hemos mutado la referencia original
// {
// name: 'Miguel',
// twitter: '@midudev',
// experience: { years: 19, focus: 'javascript' }
// }
重要である理由
Reactでは、配列に新しい要素を追加したり、変更せずに新しいオブジェクトを作成したりする必要があるのはごく普通のことです。Rest演算子は、これを実現するのに役立ちます。JavaScriptにおける価値と参照の概念がよくわからない場合は、それをレビューすると便利です。
構文は、JavaScript で関数のパラメーターで長い間機能してきました。この手法は rest パラメーターと呼ばれ、関数内に不特定多数の引数を持ち、後で配列としてそれらにアクセスできるようにしました。
...
function suma(...allArguments) {
return allArguments.reduce((previous, current) => {
return previous + current
})
}
これで、rest演算子を使用して、オブジェクトまたは反復可能オブジェクトの残りのプロパティをグループ化することもできます。これは、オブジェクトまたは反復可能オブジェクトから特定の要素を抽出し、残りの部分の表面的なコピーを新しい変数に作成する場合に便利です。
const midu = {
name: 'Miguel',
twitter: '@midudev',
experience: {
years: 18,
focus: 'javascript'
}
}
const {name, ...restOfMidu} = midu
console.log(restOfMidu)
// -> {
// twitter: '@midudev',
// experience: {
// years: 18,
// focus: 'javascript'
// }
// }
また、配列でも動作します。
const [firstNumber, ...restOfNumbers] = [1, 2, 3]
console.log(firstNumber) // -> 1
console.log(restOfNumbers) // -> [2, 3]
重要である理由
これは、オブジェクトからプロパティを(比喩的に)削除し、残りのプロパティの表面的なコピーを作成する興味深い方法です。いくつかのパラメータから必要な情報を抽出し、残りを別のレベルに渡すオブジェクトに残すのが興味深い場合があります。
オプションのチェーン演算子を使用すると、オブジェクトのさまざまなレベル内にネストされているプロパティの値を安全に読み取ることができます。
?.
このように、プロパティにアクセスするためにプロパティが存在するかどうかを確認する代わりに、オプションのチェーンを使用します。
const author = {
name: 'Miguel',
libro: {
name: 'Aprendiendo React'
},
writeBook() {
return 'Writing!'
}
};
// sin optional chaining
(author === null || author === undefined)
? undefined
: (author.libro === null || author.libro === undefined)
? undefined
: author.libro.name
// con optional chaining
author?.libro?.name
重要である理由
オブジェクトは、多くの UI 要素を表すのに最適なデータ構造です。アイテムを手に入れましたか?記事のすべての情報は確かにオブジェクトで表されます。
UIが大きく複雑になるにつれて、これらのオブジェクトにはより多くの情報が含まれるようになり、保証付きで情報にアクセスできるようにするには、オプションのチェーンを習得する必要があります。
?.
カスタムフックは、単語で始まる関数であり、他のフックで使用できます。これらは、さまざまなコンポーネントでロジックを再利用するのに理想的です。たとえば、カウンターの状態管理を抽出するカスタムフックを作成できます。
use
// ./hooks/useCounter.js
export function useCounter() {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)
const decrement = () => setCount(count - 1)
return { count, increment, decrement }
}
コンポーネントで使用するには:
import { useCounter } from './hooks/useCounter.js'
function Counter() {
const { count, increment, decrement } = useCounter()
return (
<>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
</>
)
}
useEffect
通常、Reactのコンポーネントには1つだけありますが、コンポーネントに必要な数だけ含めることができるということです。それぞれは、コンポーネントがレンダリングされたとき、またはエフェクトの依存関係が変更されたときに実行されます。
useEffect
useEffect
フックを使用してコンポーネントが逆アセンブルされたときにコードを実行し、内部で実行するコードを含む関数を返すことができます。この場合、の最初のパラメーターとして渡された関数は、コンポーネントがマウントされたときに実行され、返される関数は、アンマウントされたときに実行されます。
useEffect
useEffect
import { useEffect } from 'react'
function Component() {
useEffect(() => {
console.log('El componente se ha montado')
return () => {
console.log('El componente se ha desmontado')
}
}, [])
return <h1>Ejemplo</h1>
}
これは、ブラウザイベントなど、コンポーネントで作成されたリソースをクリーニングしたり、APIへのリクエストをキャンセルしたりする場合に非常に便利です。
useEffect
API にリクエストを行うときに、この例のように、コンポーネントがアンマウントされたときに実行されないようにキャンセルできます。
AbortController
useEffect(() => {
// Creamos el controlador para abortar la petición
const controller = new AbortController()
// Recuperamos la señal del controlador
const { signal } = controller
// Hacemos la petición a la API y le pasamos como options la señal
fetch('https://jsonplaceholder.typicode.com/posts/1', { signal })
.then(res => res.json())
.then(json => setMessage(json.title))
.catch(error => {
// Si hemos cancelado la petición, la promesa se rechaza
// con un error de tipo AbortError
if (error.name !== 'AbortError') {
console.error(error.message)
}
})
// Si se desmonta el componente, abortamos la petición
return () => controller.abort()
}, [])
これは以下でも機能します。
axios
useEffect(() => {
// Creamos el controlador para abortar la petición
const controller = new AbortController()
// Recuperamos la señal del controlador
const { signal } = controller
// Hacemos la petición a la API y le pasamos como options la señal
axios
.get('https://jsonplaceholder.typicode.com/posts/1', { signal })
.then(res => setMessage(res.data.title))
.catch(error => {
// Si hemos cancelado la petición, la promesa se rechaza
// con un error de tipo AbortError
if (error.name !== 'AbortError') {
console.error(error.message)
}
})
// Si se desmonta el componente, abortamos la petición
return () => controller.abort()
}, [])
Reactのフックには2つの基本的なルールがあります。
useEffect
useLayoutEffect
どちらも非常に似ていますが、実行される時間にわずかな違いがあります。
useLayoutEffectレンダリング後にReactがDOMを完全に更新した直後に同期的に実行されます。DOMから要素を取得し、その寸法または画面上の位置にアクセスする必要がある場合に便利です。
useEffectレンダリング後に非同期的に実行されますが、DOM が更新されているとは限りません。つまり、DOM から要素を回復し、画面上のその寸法または位置にアクセスする必要がある場合、DOM が更新されたという保証がないため、これを行うことはできません。
useEffect
通常、99%の確率でそれを使用したいと思うでしょう、そしてさらに、それはレンダリングをブロックしないので、それはより良いパフォーマンスを持っています。
useEffect
フックはReact 16.8.0に含まれていたため、関数コンポーネントはクラスコンポーネントが実行できるほとんどすべてのことを実行できます。
この質問に対する明確な答えはありませんが、機能コンポーネントは通常、読み取りと書き込みが簡単で、全体的にパフォーマンスが向上する可能性があります。
さらに、フックは機能コンポーネントでのみ使用できます。カスタムフックを作成することで、ロジックを再利用でき、コンポーネントを簡素化できるため、これは重要です。
一方、クラスコンポーネントを使用すると、コンポーネントのライフサイクルを使用できますが、これは、使用できる機能コンポーネントでは実行できません。
useEffect
参照:
純粋なコンポーネントは、状態や副作用のないコンポーネントです。これは、インターフェイスをレンダリングする以外のロジックがないことを意味します。
テストと保守が簡単です。さらに、複雑なロジックがないため、理解しやすくなります。
Reactで純粋なコンポーネントを作成するには、関数を使用します。
function Button({ text }) {
return (
<button>
{text}
</button>
)
}
この場合、コンポーネントは文字列である prop を受け取ります。コンポーネントは、prop で受け取ったテキストを含むボタンをレンダリングします。
Button
text
Button
text
サーバー上でアプリケーションをレンダリングすると、Reactは静的HTMLを生成します。この静的 HTML は、ページに表示される HTML を含む単なる文字列です。
ブラウザーは、静的 HTML を受信すると、ページにレンダリングします。ただし、この静的 HTML には対話機能がありません。イベント、ロジック、状態などはありません。命がないと言えます。
この静的HTMLをインタラクティブにするために、Reactは静的HTMLをReactコンポーネントツリーにする必要があります。これは水分補給と呼ばれます。
このように、クライアントでは、Reactはこの静的HTMLを再利用し、イベントを要素に添付し、コンポーネントに対する効果を実行し、コンポーネントの状態を調整することに専念します。
サーバーサイドレンダリングは、サーバー上でHTMLをレンダリングしてクライアントに送信する手法です。これにより、ユーザーは JavaScript が読み込まれる前にアプリのインターフェイスを表示できます。
この手法により、ユーザーエクスペリエンスを向上させ、アプリケーションのSEOを向上させることができます。
Reactでサーバーサイドレンダリングを最初から作成するには、サーバー上でReactコンポーネントをレンダリングできるパッケージを使用できます。
react-dom/server
Expressを使用してReactでサーバーサイドレンダリングを最初から作成する方法の例を見てみましょう。
import express from 'express'
import React from 'react'
import { renderToString } from 'react-dom/server'
const app = express()
app.get('/', (req, res) => {
const html = renderToString(<h1>Hola mundo</h1>)
res.send(html)
})
これにより、パスにアクセスするときにアプリケーションの HTML が返されます。
/
<h1 data-reactroot="">Hola mundo</h1>
JavaScript 関数と同様に、React コンポーネントにも副作用があります。副作用とは、コンポーネントがそのスコープ内にない情報を操作または読み取ることを意味します。
ここでは、副作用のあるコンポーネントの簡単な例を見ることができます。コンポーネントの外部にある変数を読み取って変更するコンポーネント。これにより、コンポーネントが持つ値がわからないため、コンポーネントが使用されるたびに何をレンダリングするかを知ることができなくなります。
count
let count = 0
function Counter() {
count = count + 1
return (
<p>Contador: {count}</p>
)
}
export default function Counters() {
return (
<>
<Counter />
<Counter />
<Counter />
</>
)
Reactでフォームを操作する場合、制御されたコンポーネントと制御されていないコンポーネントの2種類のコンポーネントがあります。
制御されたコンポーネント:コンポーネントの値を制御する状態を持つコンポーネントです。したがって、状態が変更されると、コンポーネントの値は更新されます。
これらのタイプのコンポーネントの利点は、インターフェイスに依存しないため、テストが容易であることです。また、検証を非常に簡単に作成することもできます。欠点は、作成と保守がより複雑になることです。さらに、入力の値が変更されるたびに再レンダリングが発生するため、パフォーマンスが低下する可能性があります。
制御されていないコンポーネント:コンポーネントの値を制御する状態を持たないコンポーネントです。コンポーネントのステータスは、ブラウザによって内部的に制御されます。コンポーネントの値を知るには、DOM の値を読み取る必要があります。
これらのタイプのコンポーネントの利点は、非常に簡単に作成でき、状態を維持する必要がないことです。さらに、入力の値を変更するときに再レンダリングする必要がないため、パフォーマンスが向上します。欠点は、DOMを直接処理し、命令型コードを作成する必要があることです。
// Controlado:
const [value, setValue] = useState('')
const handleChange = () => setValue(event.target.value)
<input type="text" value={value} onChange={handleChange} />
// No controlado:
<input type="text" defaultValue="foo" ref={inputRef} />
// Usamos `inputRef.current.value` para leer el valor del input
高次コンポーネントは、コンポーネントをパラメーターとして受け取り、コンポーネントを返す関数です。
function withLayout(Component) {
return function(props) {
return <main>
<section>
<Component {...props} />
</section>
</main>
}
}
この場合、関数はコンポーネントをパラメーターとして受け取り、コンポーネントを返します。返されたコンポーネントは、レイアウト内のパラメータとして渡されたコンポーネントをレンダリングします。
withLayout
これは、コードを再利用できるパターンであるため、簡単な方法で機能、スタイルなどをコンポーネントに挿入できます。
フックの出現により、HOCはあまり人気がなくなりましたが、それでも使用されている場合もあります。
これらは、コンポーネント間でコードを再利用し、コンポーネントのレンダリングに情報を挿入できるようにするReactデザインパターンです。
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>
この場合、コンポーネントは prop として関数を受け取ります。そこでは、パラメーターとして受け取った情報を使用して何をレンダリングする必要があるかを説明します。
DataProvider
render
with 関数の実装は次のようになります。
DataProvider
function DataProvider({ render }) {
const data = { target: 'world' }
return render(data)
}
このパターンは、コンポーネントの小道具を使用して見つけることもできます。
children
<DataProvider>
{data => (
<h1>Hello {data.target}</h1>
)}
</DataProvider>
実装は似ています。
function DataProvider({ children }) {
const data = { target: 'world' }
return children(data)
}
このパターンは、 や などの大規模なライブラリで使用されます。
react-router
formik
react-motion
if
Reactでは、有効なJavaScript式ではなく宣言であるため、コンポーネントのレンダリングにaを使用することはできません。式は値を返す式であり、宣言は値を返しません。
if
JSXでは式しか使用できないため、式である三項を使用します。
// ❌ Esto no funciona
function Button({ text }) {
return (
<button>
{if (text) { return text } else { return 'Click' }}
</button>
)
}
// ✅ Esto funciona
function Button({ text }) {
return (
<button>
{text ? text : 'Click'}
</button>
)
}
同様に、 、またはコンポーネントのレンダリング内では使用できません。
for
while
switch
Reactのステータスを更新するときは、フックによって提供される関数を使用してステータスを更新する必要があります。
useState
const [count, setCount] = useState(0)
setCount(count + 1)
なぜこれが必要なのですか?まず第一に、Reactのステータスは不変でなければなりません。つまり、状態を直接変更することはできませんが、常に新しい状態の新しい値を作成する必要があります。
これにより、レンダリングするデータに関するUIの整合性が常に正しいものになります。
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 tantas veces como queramos y React se encargará de actualizar el estado cuando lo considere oportuno.
setCount
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.
index
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 . Esto puede ser un problema si la lista se reordena o se eliminan elementos del array, ya que el índice de los elementos cambia.
key
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>
)
}
useMemo
El hook 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.
useMemo
import { useMemo } from 'react'
function Counter({ count }) {
const double = useMemo(() => count * 2, [count])
return (
<div>
<p>Contador: {count}</p>
<p>Doble: {double}</p>
</div>
)
}
En este caso, el componente recibe una prop que es un número. El componente calcula el doble de ese número y lo muestra en pantalla.
Counter
count
El hook 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 .
useMemo
count
La ventaja es que si la prop no cambia, se evita el cálculo del doble y se devuelve el valor que ya se había calculado previamente.
count
useMemo
No. 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.
useMemo
useCallback
El hook 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.
useCallback
import { useCallback } from 'react'
function Counter({ count, onIncrement }) {
const handleIncrement = useCallback(() => {
onIncrement(count)
}, [count, onIncrement])
return (
<div>
<p>Contador: {count}</p>
<button onClick={handleIncrement}>Incrementar</button>
</div>
)
}
En este caso, el componente recibe una prop que es un número y una prop que es una función que se ejecuta cuando se pulsa el botón.
Counter
count
onIncrement
El hook 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 o la prop .
useCallback
count
onIncrement
La ventaja es que si la prop o la prop 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.
count
onIncrement
useCallback
No. 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.
useCallback
useCallback
useMemo
La diferencia entre y es que memoriza una función y memoriza el resultado de una función.
useCallback
useMemo
useCallback
useMemo
En cualquier caso, en realidad, es una versión especializada de . De hecho se puede simular la funcionalidad de con :
useCallback
useMemo
useCallback
useMemo
const memoizedCallback = useMemo(() => {
return () => {
doSomething(a, b)
}
}, [a, b])
Las refs nos permiten crear una referencia a un elemento del DOM o a un valor que se mantendrá entre renderizados. Se pueden declarar por medio del comando o con el hook .
createRef
useRef
useRef
En el siguiente ejemplo vamos a guardar la referencia en el DOM a un elemento y vamos a cambiar el foco a ese elemento cuando hacemos clic en el botón.
<input>
import { useRef } from 'react'
function TextInputWithFocusButton() {
const inputEl = useRef(null)
const onButtonClick = () => {
// `current` apunta al elemento inputEl montado
inputEl.current.focus()
}
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
)
}
Creamos una referencia con y la pasamos al elemento como prop . Cuando el componente se monta, la referencia apunta al elemento del DOM.
inputEl
useRef
<input>
ref
inputEl
<input>
Para acceder al elemento del DOM, usamos la propiedad de la referencia.
current
useLayoutEffect
useLayoutEffectfunciona igual que el hook , con la excepción de que este se dispara sincrónicamente después de leer todas las mutaciones del DOM.
useEffect
Llama en el nivel superior del componente.
useLayoutEffect
import { useLayoutEffect } from 'react';
useLayoutEffect(() => {
return () => {
}
}, []);
useLayoutEffectrecibe dos argumentos:
Aunque el 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 .
useEffect
useLayoutEffect
useLayoutEffect
El orden de ejecución del , 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:
useLayoutEffect
Los componentes stateless son componentes que no tienen estado. Estos componentes se crean con una 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).
function
// Este es un ejemplo de componente stateless
function Button({ text }) {
return (
<button>
{text}
</button>
)
}
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>
}
StrictMode
El 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.
StrictMode
import { StrictMode } from 'react'
function App() {
return (
<StrictMode>
<Component />
</StrictMode>
)
}
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 />
}
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>
}
Para importar de forma dinámica un componente en React debemos usar la función , el método de React y el componente .
import()
lazy()
Suspense
// App.jsx
import { lazy, Suspense } from 'react'
const Button = lazy(() => import('./button.jsx'))
export default function App() {
return (
<Suspense fallback={<div>Cargando botón...</div>}>
<Button />
</Suspense>
)
}
// button.jsx
export default function Button() {
return <button>Botón cargado dinámicamente</button>
}
Vamos a ver en detalle cada uno de los elementos que hemos usado:
La función 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.
import()
El método 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 . Ten en cuenta que el componente que devuelve debe ser un componente de React y ser exportado por defecto ().
lazy()
button.jsx
lazy()
export default
El componente nos permite mostrar un mensaje mientras se está cargando el componente. Este componente recibe una prop que es el mensaje que se muestra mientras se está cargando el componente.
Suspense
fallback
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 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.
SuperBigModal
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 } 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 se carga de forma dinámica, es decir, cuando el usuario da click en el botón para mostrar la modal.
SuperBigModal
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?
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>
)
}
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>
}
SyntheticEvent
El es una abstracción del evento nativo del navegador. Esto le permite a React tener un comportamiento consistente en todos los navegadores.
SyntheticEvent
Dentro del puede encontrarse una referencia al evento nativo en su atributo
SyntheticEvent
nativeEvent
function App() {
function handleClick(event) {
console.log(event)
}
return <button onClick={handleClick}>Haz clic aquí</button>
}
flushSync
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 actualizada ✅
flushSync(() => {
setName("John")
// component no hace re-render 🚫
setEmail("john@doe.com")
// component re-renderiza aquí 🔄
})
// component ha sido re-renderizado y el DOM ha sido actualizada ✅
}
return <button onClick={handleClick}>Haz clic aquí</button>
}
NOTA: puede afectar significativamente el rendimiento. Úsalo con moderación.
flushSync
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.
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 ) y la recupera el componente padre (), para poder hacer focus en él gracias al uso de en el componente hijo ().
<button>
Parent
forwardRef
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.
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 y se puede instalar con .
prop-types
npm install prop-types
import PropTypes from "prop-types"
function App(props) {
return <h1>{props.title}</h1>
}
App.propTypes = {
title: PropTypes.string.isRequired,
}
En este ejemplo, estamos validando que la prop sea de tipo y que sea obligatoria.
title
string
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 para indicar que es obligatoria.
isRequired
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.
Para validar las propiedades de un objeto que se pasa como prop, podemos usar la propiedad de :
shape
PropTypes
import PropTypes from "prop-types"
function App({ title }) {
const { text, color } = title
return <h1 style={{ color }}>{text}</h1>
}
App.propTypes = {
title: PropTypes.shape({
text: PropTypes.string.isRequired,
color: PropTypes.string.isRequired,
}),
}
Para validar las propiedades de un array que se pasa como prop, podemos usar la propiedad de :
arrayOf
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 sea un array y que cada uno de sus elementos sea un objeto con la propiedad de tipo . Además, la prop es obligatoria.
items
text
string
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.
Digamos que tenemos un componente que recibe un objeto con todas las props que necesita:
App
props
function App(props) {
return <h1>{props.title}</h1>
}
Y que tenemos otro componente que recibe un objeto con todas las props que necesita:
Layout
props
function Layout(props) {
return (
<div>
<App {...props} />
</div>
)
}
En este caso, está pasando todas las props que recibe a . Esto puede ser una mala idea por varias razones:
Layout
App
Layout
App
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 en tu aplicación, y también necesitarás otra biblioteca o framework para solucionar esto.
<head>
Otra diferencia es que React no está opinionado sobre qué empaquetador de aplicaciones usar. En cambio en su propio tutorial ya te indica que debes usar para crear una aplicación, en cambio React siempre te deja la libertad de elegir qué empaquetador usar y ofrece diferentes opciones.
Angular
@angular/cli
Aún así, existe gente que considera a React como un framework. Aunque no hay una definición oficial de qué es un framework, la mayoría de la gente considera que un framework es una biblioteca que incluye otras bibliotecas para crear una aplicación completa de forma opinionada y casi sin configuración.
Por ejemplo, Next.js se podría considerar un framework de React porque incluye React, un sistema de enrutado, un sistema de renderizado del lado del servidor, etc.
useImperativeHandle
Nos permite definir qué propiedades y métodos queremos que sean accesibles desde el componente padre.
En el siguiente ejemplo vamos a crear un componente que tiene un método que cambia el foco al elemento .
TextInput
focus
<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 con y la pasamos al elemento como prop . Cuando el componente se monta, la referencia apunta al elemento del DOM.
inputEl
useRef
<input>
ref
inputEl
<input>
Para acceder al elemento del DOM, usamos la propiedad de la referencia.
current
Para que el componente padre pueda acceder al método , usamos el hook . 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.
focus
useImperativeHandle
cloneElement
Te permite clonar un elemento React y añadirle o modificar las props que recibe.
import { cloneElement } from 'react'
const Hello = ({ name }) => <h1>Hello {name}</h1>
const App = () => {
const element = <Hello name="midudev" />
return (
<div>
{cloneElement(element, { name: 'TMChein' })}
{cloneElement(element, { name: 'Madeval' })}
{cloneElement(element, { name: 'Gorusuke' })}
</div>
)
}
En este ejemplo, clonamos que tenía la prop y le pasamos una prop diferente cada vez. Esto renderizará tres veces el componente con los nombres , y . Sin rastro de la prop original.
element
midudev
name
Hello
TMChein
Madeval
Gorusuke
Puede ser útil para modificar un elemento que ya nos viene de un componente padre y del que no tenemos posibilidad de re-crear con el componente.
Los portales nos permiten renderizar un componente en un nodo del DOM que no es hijo del componente que lo renderiza.
Es perfecto para ciertos casos de uso como, por ejemplo, modales:
import { createPortal } from 'react-dom'
function Modal() {
return createPortal(
<div className="modal">
<h1>Modal</h1>
</div>,
document.getElementById('modal')
)
}
createPortalacepta dos parámetros:
En este caso el modal se renderiza en el nodo del DOM.
#modal
StrictMode
Cuando el modo 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.
StrictMode
Como developers, nuestra misión es encontrar el equilibrio entre rendimiento y experiencia, intentando priorizar siempre cómo el usuario sentirá la aplicación. No hay ningún caso lo suficientemente justificado para renderizar en pantalla miles de datos.
El espacio de visualización es limitado (viewport), al igual que deberían serlo los datos que añadimos al DOM.
useEffect
Si quieres evitar que exista una race condition entre una petición asíncrona y que el componente se desmonte, puedes usar la API de para abortar la petición cuando lo necesites:
AbortController
import { useEffect, useState } from 'react'
function Movies () {
const [movies, setMovies] = useState([])
useEffect(() => {
// creamos un controlador para abortar la petición
const abortController = new AbortController()
// pasamos el signal al fetch para que sepa que debe abortar
fetchMovies({ signal: abortController.signal })
.then(() => {
setMovies(data.results)
}).catch(error => {
if (error.name === 'AbortError') {
console.log('fetch aborted')
}
})
return () => {
// al desmontar el componente, abortamos la petición
// sólo funcionará si la petición sigue en curso
abortController.abort()
}
})
// ...
}
// Debemos pasarle el parámetro signal al `fetch`
// para que enlace la petición con el controlador
const fetchMovies = ({ signal }) => {
return fetch('https://api.themoviedb.org/3/movie/popular', {
signal // <--- pasamos el signal
}).then(response => response.json())
}
De esta forma evitamos que se produzca un error por parte de React de intentar actualizar el estado de un componente que ya no existe, además de evitar que se produzcan llamadas innecesarias al servidor.
En lugar de recibir la lista en una sola llamada a la API (lo cual sería negativo tanto para el rendimiento como para el propio servidor y tiempo de respuesta de la API), podríamos implementar un sistema de paginación en el cual la API recibirá un offset o rango de datos deseados. En el FE nuestra responsabilidad es mostrar unos controles adecuados (interfaz de paginación) y gestionar las llamadas a petición de cambio de página para siempre limitar la cantidad de DOM renderizado evitando así una sobrecarga del DOM y, por lo tanto, problemas de rendimiento.
Existe una técnica llamada Virtualización que gestiona cuántos elementos de una lista mantenemos vivos en el DOM. El concepto se basa en solo montar los elementos que estén dentro del viewport más un buffer determinado (para evitar falta de datos al hacer scroll) y, en cambio, desmontar del DOM todos aquellos elementos que estén fuera de la vista del usuario. De este modo podremos obtener lo mejor de los dos mundos, una experiencia integrada y un DOM liviano que evitará posibles errores de rendimiento. Con esta solución también podremos aprovechar que contamos con los datos en memoria para realizar búsquedas/filtrados sin necesidad de más llamadas al servidor.
Puedes consultar esta librería para aplicar Virtualización con React: React Virtualized.
Hay que tener en cuenta que cada caso de uso puede encontrar beneficios y/o perjuicios en ambos métodos, dependiendo de factores como capacidad de respuesta de la API, cantidad de datos, necesidad de filtros complejos, etc. Por ello es importante analizar cada caso con criterio.
useDebugValue
Nos permite mostrar un valor personalizado en la pestaña de React DevTools que nos permitirá depurar nuestro código.
import { useDebugValue } from 'react'
function useCustomHook() {
const value = 'custom value'
useDebugValue(value)
return value
}
En este ejemplo, el valor personalizado que se muestra en la pestaña de React DevTools es .
custom value
Aunque es útil para depurar, no se recomienda usar este hook en producción.
Profiler
El es un componente que nos permite medir el tiempo que tarda en renderizarse un componente y sus hijos.
Profiler
import { Profiler } from 'react'
function App() {
return (
<Profiler id="App" onRender={(id, phase, actualDuration) => {
console.log({id, phase, actualDuration})
}}>
<Component />
</Profiler>
)
}
El componente recibe dos parámetros:
Profiler
id: es un identificador único para el componente
onRender: es una función que se ejecuta cada vez que el componente se renderiza
Esta información es muy útil para detectar componentes que toman mucho tiempo en renderizarse y optimizarlos.
React no expone el evento nativo del navegador. En su lugar, React crea un objeto sintético que se basa en el evento nativo del navegador llamado . Para acceder al evento nativo del navegador, debemos usar el atributo :
SyntheticEvent
nativeEvent
function Button({ onClick }) {
return <button onClick={e => onClick(e.nativeEvent)}>Haz clic aquí</button>
}
En React, los eventos se registran en la fase de burbuja por defecto. Para registrar un evento en la fase de captura, debemos añadir al nombre del evento:
Capture
function Button({ onClick }) {
return <button onClickCapture={onClick}>Haz clic aquí</button>
}
Aunque puedes usar el método 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 :
renderToString
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="https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/master/clientrender.js"></script>'
)
},
onAllReady() {
// Si no quieres hacer streaming de los datos, puedes usar
// esto en lugar de onShellReady. Esto se ejecuta cuando
// todo el HTML está listo para ser enviado.
// Perfecto para crawlers o generación de sitios estáticos
// res.statusCode = didError ? 500 : 200
// res.setHeader('Content-type', 'text/html')
// stream.pipe(res)
},
onError(err) {
didError = true
console.error(err)
},
}
)
renderToStaticNodeStream()
renderToPipeableStream()
renderToStaticNodeStream()devuelve un stream de nodos estáticos, esto significa que no añade atributos extras para el DOM que React usa internamente para poder lograr la hidratación del HTML en el cliente. Esto significa que no podrás hacer el HTML interactivo en el cliente, pero puede ser útil para páginas totalmente estáticas.
renderToPipeableStream()devuelve un stream de nodos que contienen atributos del DOM extra para que React pueda hidratar el HTML en el cliente. Esto significa que podrás hacer el HTML interactivo en el cliente pero puede ser más lento que .
renderToStaticNodeStream()
useDeferredValue
El hook 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.
useDeferredValue
function App() {
const [text, setText] = useState('¡Hola mundo!')
const deferredText = useDeferredValue(text, { timeoutMs: 2000 })
return (
<div className='App'>
{/* Seguimos pasando el texto actual como valor del input */}
<input value={text} onChange={handleChange} />
...
{/* Pero la lista de resultados se podría renderizar más tarde si fuera necesario */}
<MySlowList text={deferredText} />
</div>
)
}
renderToReadableStream()
Este método es similar a , pero está pensado para entornos que soporten Web Streams como .
renderToNodeStream
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="https://raw.githubusercontent.com/midudev/preguntas-entrevista-react/master/clientrender.js"></script>',
{
status: 500,
headers: {'Content-Type': 'text/html'},
}
)
}
Para hacer testing de un componente, puedes usar la función de la librería . Esta función nos permite renderizar un componente y obtener el resultado.
render
@testing-library/react
import { render } from '@testing-library/react'
function Counter() {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
)
}
test('Counter', () => {
const { getByText } = render(<Counter />)
expect(getByText('Count: 0')).toBeInTheDocument()
fireEvent.click(getByText('Increment'))
expect(getByText('Count: 1')).toBeInTheDocument()
})
Para hacer testing de un hook, puedes usar la función de la librería . Esta función nos permite renderizar un hook y obtener el resultado.
renderHook
@testing-library/react-hooks
import { renderHook } from '@testing-library/react-hooks'
function useCounter() {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)
return { count, increment }
}
test('useCounter', () => {
const { result } = renderHook(() => useCounter())
expect(result.current.count).toBe(0)
act(() => {
result.current.increment()
})
expect(result.current.count).toBe(1)
})
Flux es un patrón de arquitectura de aplicaciones que se basa en un unidireccional de datos. En este patrón, los datos fluyen en una sola dirección: de las vistas a los stores.
No es específico de React y se puede usar con cualquier librería de vistas. En este patrón, los stores son los encargados de almacenar los datos de la aplicación. Los stores emiten eventos cuando los datos cambian. Las vistas se suscriben a estos eventos para actualizar los datos.
Esta arquitectura fue creada por Facebook para manejar la complejidad de sus aplicaciones. Redux se basó en este patrón para crear una biblioteca de gestión de estado global.
Es un error bastante común en React y que puede parecernos un poco extraño si estamos empezando a aprender esta tecnología. Por suerte, es bastante sencillo de solucionar.
Básicamente, este mensaje aparece en la consola cuando estamos renderizando un listado dentro de nuestro componente, pero no le estamos indicando la propiedad "key". React usa esta propiedad para determinar qué elemento hijo dentro de un listado ha sufrido cambios, por lo que funciona como una especie de identificativo.
De esta manera, React utiliza esta información para identificar las diferencias existentes con respecto al DOM y optimizar la renderización del listado, determinando qué elementos necesitan volverse a calcular. Esto habitualmente pasa cuando agregamos, eliminamos o cambiamos el orden de los items en una lista.
Recomendamos revisar las siguientes secciones:
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 , ni un loop, ni tampoco dentro de una función anónima. Siempre deben estar en el nivel superior de la función.
if
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 de forma condicional (pese a no estar dentro de un ) ya que se ejecutará sólo cuando sea diferente a :
useState
if
count
0
// ❌ código incorrecto por saltar las reglas de los hooks
function Counter() {
const [count, setCount] = useState(0)
// si count es 0, no se ejecuta el siguiente hook useState
// ya que salimos de la ejecución aquí
if (count === 0) return null
const [name, setName] = useState('midu')
return <div>{count} {name}</div>
}
Ten en cuenta que si ignoras este error, es posible que tus componentes no se comporten de forma correcta y tengas comportamientos no esperados en el funcionamiento de tus componentes.
Para arreglar este error, como hemos comentado antes, debes asegurarte de que los hooks se llaman en el mismo orden en cada renderizado. El último ejemplo quedaría así:
function Counter() {
const [count, setCount] = useState(0)
// movemos el hook useState antes del if
const [name, setName] = useState('midu')
if (count === 0) return null
return <div>{count} {name}</div>
}
Recomendamos revisar las siguientes secciones:
Este error se produce cuando intentamos actualizar el estado de un componente que ya no está montado. Esto puede ocurrir cuando el componente se desmonta antes de que se complete una petición asíncrona, por ejemplo:
function Movies () {
const [movies, setMovies] = useState([])
useEffect(() => {
fetchMovies().then(() => {
setMovies(data.results)
})
})
if (!movies.length) return null
return (
<section>
{movies.map(movie => (
<article key={movie.id}>
<h2>{movie.title}</h2>
<p>{movie.overview}</p>
</article>
))}
</section>
)
}
Parece un código inofensivo, pero imagina que usamos este componente en una página. Si el usuario navega a otra página antes de que se complete la petición, el componente se desmontará y React lanzará el error, ya que intentará ejecutar el en un componente (Movies) que ya no está montado.
setMovies
Para evitar este error, podemos usar una variable booleana con que nos indique si el componente está montado o no. De esta manera, podemos evitar que se ejecute el si el componente no está montado:
useRef
setMovies
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 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.
AbortController
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 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.
setCount
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 que actualiza el estado en cada renderizado del componente, lo que provoca renderizaciones infinitas.
setCount
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 , estamos provocando que el código que se encuentre dentro se ejecute en cada renderizado del componente. Al llamar al y actualizar el estado, obtenemos nuevamente renderizaciones infinitas.
useEffect
setCounter
これを修正するには、次の操作を行います。
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>
}
これらは、コードでこのエラーメッセージに遭遇したときに見つけることができる考えられる原因のほんの一部です。この情報を補足する場合は、次のセクションを確認することをお勧めします。
シャドウ DOM は、DOM の要素内に DOM ノードの個別のツリーを作成できるブラウザー API です。これにより、アプリケーションの他の部分と干渉しないコンポーネントを作成できます。特に Web コンポーネントで使用されます。
仮想 DOM は、メモリ内の DOM の表現です。この表現は、DOM に変更があるたびに作成されます。これにより、現在のDOMを以前のDOMと比較して、実際のDOMにどのような変更を加える必要があるかを判断できます。これは、実際のDOMに最小限の変更を加えるためにReactやその他のライブラリによって使用されます。