Publicado · en mejora
Guía de React · 3/6
Por ahora, este capítulo solo está disponible en inglés.
A React app is built by composing components. This chapter covers components and JSX, props and state, the hooks you will use most, rendering lists with keys, and how to compose components.
A component is a JavaScript function that returns JSX. JSX looks like HTML but compiles to JavaScript, so it follows a few rules:
<>...</>.className instead of class, onClick instead of onclick.<img /> and <input />.{} embed any JavaScript expression, such as a variable or a function call.const user = { name: "Hedy Lamarr", avatarUrl: "/avatars/hedy.png" };
export function Profile() {
return (
<>
<h2 className="title">{user.name}</h2>
<img src={user.avatarUrl} alt={`Photo of ${user.name}`} width={64} />
</>
);
}Props are read-only inputs that a parent component passes to a child. Typing them in TypeScript catches incorrect usage at compile time. The special children prop receives whatever you nest between the opening and closing tags.
import type { ReactNode } from "react";
type CardProps = {
title: string;
variant?: "default" | "warning";
children: ReactNode;
};
export function Card({ title, variant = "default", children }: CardProps) {
return (
<section className={`card card-${variant}`}>
<h3>{title}</h3>
{children}
</section>
);
}
// Usage: <Card title="Heads up" variant="warning"><p>Unsaved changes.</p></Card>State is a value a component remembers between renders. useState returns the current value and a setter; calling the setter tells React to re-render the component. Never mutate state directly: always replace it with a new value, and copy objects or arrays with the spread syntax.
import { useState } from "react";
export function ItemAdder() {
const [count, setCount] = useState(0);
const [items, setItems] = useState<string[]>([]);
function addItem() {
setItems([...items, `Task ${items.length + 1}`]); // a new array
setCount((c) => c + 1); // updater function when the next value depends on the previous one
}
return <button onClick={addItem}>Add ({count})</button>;
}Use if, the ternary operator or && to render different output depending on a condition. Turn arrays into JSX with map, and give each item a that is unique among its siblings and stable over time. React uses keys to match items between renders, so using the array index as a key in a list that can be reordered may attach state to the wrong item.
type Todo = { id: string; text: string; done: boolean };
export function TodoList({ todos }: { todos: Todo[] }) {
if (todos.length === 0) return <p>Nothing to do.</p>;
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>
{todo.text} {todo.done && "✓"}
</li>
))}
</ul>
);
}useEffect synchronizes with external systems (timers, browser APIs, subscriptions) after rendering. It re-runs whenever a value in its dependency array changes, and the cleanup function it returns runs before the next run and when the component unmounts.useMemo caches the result of an expensive calculation until its dependencies change.useRef holds a value that does not trigger a re-render, or gives you access to a DOM element.import { useEffect, useMemo, useRef, useState } from "react";
export function SearchBox({ words }: { words: string[] }) {
const [query, setQuery] = useState("");
const [seconds, setSeconds] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
inputRef.current?.focus();
const id = setInterval(() => setSeconds((s) => s + 1), 1000);
return () => clearInterval(id); // cleanup
}, []);
const matches = useMemo(
() => words.filter((w) => w.includes(query)),
[words, query],
);
return (
<div>
<input ref={inputRef} value={query} onChange={(e) => setQuery(e.target.value)} />
<p>{matches.length} matches · {seconds}s elapsed</p>
</div>
);
}If a value can be computed from props or state, compute it during rendering rather than in an effect. Call hooks only at the top level of a component or custom hook, never inside conditions or loops. These are the Rules of Hooks, and the ESLint plugin enforces them.
React reuses components through composition rather than inheritance. Build the frame with children or props that accept JSX, and let the caller fill in the content. Repeated stateful logic moves into custom hooks whose names start with use.
import { useState, type ReactNode } from "react";
function useToggle(initial = false) {
const [on, setOn] = useState(initial);
return [on, () => setOn((v) => !v)] as const;
}
export function Panel({ header, children }: { header: ReactNode; children: ReactNode }) {
const [open, toggle] = useToggle(true);
return (
<div className="panel">
<button onClick={toggle}>{header}</button>
{open && children}
</div>
);
}useState and is always replaced, never mutated.key values.useEffect to sync with external systems, useMemo for expensive calculations and useRef for DOM access or values that should not trigger renders.children and custom hooks instead of inheritance.
0 comentarios
Iniciar sesión · Inicia sesión para dejar un comentario.
Sé el primero en comentar.