From f428f0e6482e26b2372a3dc83e9f000211433b33 Mon Sep 17 00:00:00 2001 From: artimath Date: Thu, 6 Nov 2025 01:35:51 -0800 Subject: [PATCH 1/4] feat: Add llms.txt support for AI-friendly documentation Fixes #8117 Adds llms.txt and llms-full.txt generation following https://llmstxt.org/ spec to ensure AI models can access the latest React documentation. - llms.txt: 13KB hierarchical link index (167 pages) - llms-full.txt: 2.7MB full embedded documentation Generated from sidebar configs and markdown content at build time. Integrated into build pipeline via package.json scripts. --- package.json | 3 +- public/llms-full.txt | 92453 +++++++++++++++++++++++++++++++++ public/llms.txt | 176 + scripts/generate-llms-txt.js | 191 + 4 files changed, 92822 insertions(+), 1 deletion(-) create mode 100644 public/llms-full.txt create mode 100644 public/llms.txt create mode 100644 scripts/generate-llms-txt.js diff --git a/package.json b/package.json index c2a84b1ea29..7f6e407090f 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "analyze": "ANALYZE=true next build", "dev": "next-remote-watch ./src/content", - "build": "next build && node --experimental-modules ./scripts/downloadFonts.mjs", + "build": "next build && node --experimental-modules ./scripts/downloadFonts.mjs && node scripts/generate-llms-txt.js", "lint": "next lint && eslint \"src/content/**/*.md\"", "lint:fix": "next lint --fix && eslint \"src/content/**/*.md\" --fix", "format:source": "prettier --config .prettierrc --write \"{plugins,src}/**/*.{js,ts,jsx,tsx,css}\"", @@ -21,6 +21,7 @@ "postinstall": "yarn --cwd eslint-local-rules install && is-ci || husky install .husky", "check-all": "npm-run-all prettier lint:fix tsc rss", "rss": "node scripts/generateRss.js", + "llms": "node scripts/generate-llms-txt.js", "deadlinks": "node scripts/deadLinkChecker.js", "copyright": "node scripts/copyright.js", "test:eslint-local-rules": "yarn --cwd eslint-local-rules test" diff --git a/public/llms-full.txt b/public/llms-full.txt new file mode 100644 index 00000000000..adcb8015396 --- /dev/null +++ b/public/llms-full.txt @@ -0,0 +1,92453 @@ +# React Documentation + +> The library for web and native user interfaces + +--- +title: "Quick Start" +url: "https://react.dev/learn" +--- + + + +Welcome to the React documentation! This page will give you an introduction to 80% of the React concepts that you will use on a daily basis. + + + + + +- How to create and nest components +- How to add markup and styles +- How to display data +- How to render conditions and lists +- How to respond to events and update the screen +- How to share data between components + + + +## Creating and nesting components {/*components*/} + +React apps are made out of *components*. A component is a piece of the UI (user interface) that has its own logic and appearance. A component can be as small as a button, or as large as an entire page. + +React components are JavaScript functions that return markup: + +```js +function MyButton() { + return ( + + ); +} +``` + +Now that you've declared `MyButton`, you can nest it into another component: + +```js {5} +export default function MyApp() { + return ( +
+

Welcome to my app

+ +
+ ); +} +``` + +Notice that `` starts with a capital letter. That's how you know it's a React component. React component names must always start with a capital letter, while HTML tags must be lowercase. + +Have a look at the result: + + + +```js +function MyButton() { + return ( + + ); +} + +export default function MyApp() { + return ( +
+

Welcome to my app

+ +
+ ); +} +``` + +
+ +The `export default` keywords specify the main component in the file. If you're not familiar with some piece of JavaScript syntax, [MDN](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export) and [javascript.info](https://javascript.info/import-export) have great references. + +## Writing markup with JSX {/*writing-markup-with-jsx*/} + +The markup syntax you've seen above is called *JSX*. It is optional, but most React projects use JSX for its convenience. All of the [tools we recommend for local development](/learn/installation) support JSX out of the box. + +JSX is stricter than HTML. You have to close tags like `
`. Your component also can't return multiple JSX tags. You have to wrap them into a shared parent, like a `
...
` or an empty `<>...` wrapper: + +```js {3,6} +function AboutPage() { + return ( + <> +

About

+

Hello there.
How do you do?

+ + ); +} +``` + +If you have a lot of HTML to port to JSX, you can use an [online converter.](https://transform.tools/html-to-jsx) + +## Adding styles {/*adding-styles*/} + +In React, you specify a CSS class with `className`. It works the same way as the HTML [`class`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class) attribute: + +```js + +``` + +Then you write the CSS rules for it in a separate CSS file: + +```css +/* In your CSS */ +.avatar { + border-radius: 50%; +} +``` + +React does not prescribe how you add CSS files. In the simplest case, you'll add a [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) tag to your HTML. If you use a build tool or a framework, consult its documentation to learn how to add a CSS file to your project. + +## Displaying data {/*displaying-data*/} + +JSX lets you put markup into JavaScript. Curly braces let you "escape back" into JavaScript so that you can embed some variable from your code and display it to the user. For example, this will display `user.name`: + +```js {3} +return ( +

+ {user.name} +

+); +``` + +You can also "escape into JavaScript" from JSX attributes, but you have to use curly braces *instead of* quotes. For example, `className="avatar"` passes the `"avatar"` string as the CSS class, but `src={user.imageUrl}` reads the JavaScript `user.imageUrl` variable value, and then passes that value as the `src` attribute: + +```js {3,4} +return ( + +); +``` + +You can put more complex expressions inside the JSX curly braces too, for example, [string concatenation](https://javascript.info/operators#string-concatenation-with-binary): + + + +```js +const user = { + name: 'Hedy Lamarr', + imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg', + imageSize: 90, +}; + +export default function Profile() { + return ( + <> +

{user.name}

+ {'Photo + + ); +} +``` + +```css +.avatar { + border-radius: 50%; +} + +.large { + border: 4px solid gold; +} +``` + +
+ +In the above example, `style={{}}` is not a special syntax, but a regular `{}` object inside the `style={ }` JSX curly braces. You can use the `style` attribute when your styles depend on JavaScript variables. + +## Conditional rendering {/*conditional-rendering*/} + +In React, there is no special syntax for writing conditions. Instead, you'll use the same techniques as you use when writing regular JavaScript code. For example, you can use an [`if`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else) statement to conditionally include JSX: + +```js +let content; +if (isLoggedIn) { + content = ; +} else { + content = ; +} +return ( +
+ {content} +
+); +``` + +If you prefer more compact code, you can use the [conditional `?` operator.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) Unlike `if`, it works inside JSX: + +```js +
+ {isLoggedIn ? ( + + ) : ( + + )} +
+``` + +When you don't need the `else` branch, you can also use a shorter [logical `&&` syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND#short-circuit_evaluation): + +```js +
+ {isLoggedIn && } +
+``` + +All of these approaches also work for conditionally specifying attributes. If you're unfamiliar with some of this JavaScript syntax, you can start by always using `if...else`. + +## Rendering lists {/*rendering-lists*/} + +You will rely on JavaScript features like [`for` loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for) and the [array `map()` function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) to render lists of components. + +For example, let's say you have an array of products: + +```js +const products = [ + { title: 'Cabbage', id: 1 }, + { title: 'Garlic', id: 2 }, + { title: 'Apple', id: 3 }, +]; +``` + +Inside your component, use the `map()` function to transform an array of products into an array of `
  • ` items: + +```js +const listItems = products.map(product => +
  • + {product.title} +
  • +); + +return ( +
      {listItems}
    +); +``` + +Notice how `
  • ` has a `key` attribute. For each item in a list, you should pass a string or a number that uniquely identifies that item among its siblings. Usually, a key should be coming from your data, such as a database ID. React uses your keys to know what happened if you later insert, delete, or reorder the items. + + + +```js +const products = [ + { title: 'Cabbage', isFruit: false, id: 1 }, + { title: 'Garlic', isFruit: false, id: 2 }, + { title: 'Apple', isFruit: true, id: 3 }, +]; + +export default function ShoppingList() { + const listItems = products.map(product => +
  • + {product.title} +
  • + ); + + return ( +
      {listItems}
    + ); +} +``` + + + +## Responding to events {/*responding-to-events*/} + +You can respond to events by declaring *event handler* functions inside your components: + +```js {2-4,7} +function MyButton() { + function handleClick() { + alert('You clicked me!'); + } + + return ( + + ); +} +``` + +Notice how `onClick={handleClick}` has no parentheses at the end! Do not _call_ the event handler function: you only need to *pass it down*. React will call your event handler when the user clicks the button. + +## Updating the screen {/*updating-the-screen*/} + +Often, you'll want your component to "remember" some information and display it. For example, maybe you want to count the number of times a button is clicked. To do this, add *state* to your component. + +First, import [`useState`](/reference/react/useState) from React: + +```js +import { useState } from 'react'; +``` + +Now you can declare a *state variable* inside your component: + +```js +function MyButton() { + const [count, setCount] = useState(0); + // ... +``` + +You’ll get two things from `useState`: the current state (`count`), and the function that lets you update it (`setCount`). You can give them any names, but the convention is to write `[something, setSomething]`. + +The first time the button is displayed, `count` will be `0` because you passed `0` to `useState()`. When you want to change state, call `setCount()` and pass the new value to it. Clicking this button will increment the counter: + +```js {5} +function MyButton() { + const [count, setCount] = useState(0); + + function handleClick() { + setCount(count + 1); + } + + return ( + + ); +} +``` + +React will call your component function again. This time, `count` will be `1`. Then it will be `2`. And so on. + +If you render the same component multiple times, each will get its own state. Click each button separately: + + + +```js +import { useState } from 'react'; + +export default function MyApp() { + return ( +
    +

    Counters that update separately

    + + +
    + ); +} + +function MyButton() { + const [count, setCount] = useState(0); + + function handleClick() { + setCount(count + 1); + } + + return ( + + ); +} +``` + +```css +button { + display: block; + margin-bottom: 5px; +} +``` + +
    + +Notice how each button "remembers" its own `count` state and doesn't affect other buttons. + +## Using Hooks {/*using-hooks*/} + +Functions starting with `use` are called *Hooks*. `useState` is a built-in Hook provided by React. You can find other built-in Hooks in the [API reference.](/reference/react) You can also write your own Hooks by combining the existing ones. + +Hooks are more restrictive than other functions. You can only call Hooks *at the top* of your components (or other Hooks). If you want to use `useState` in a condition or a loop, extract a new component and put it there. + +## Sharing data between components {/*sharing-data-between-components*/} + +In the previous example, each `MyButton` had its own independent `count`, and when each button was clicked, only the `count` for the button clicked changed: + + + + + +Initially, each `MyButton`'s `count` state is `0` + + + + + +The first `MyButton` updates its `count` to `1` + + + + + +However, often you'll need components to *share data and always update together*. + +To make both `MyButton` components display the same `count` and update together, you need to move the state from the individual buttons "upwards" to the closest component containing all of them. + +In this example, it is `MyApp`: + + + + + +Initially, `MyApp`'s `count` state is `0` and is passed down to both children + + + + + +On click, `MyApp` updates its `count` state to `1` and passes it down to both children + + + + + +Now when you click either button, the `count` in `MyApp` will change, which will change both of the counts in `MyButton`. Here's how you can express this in code. + +First, *move the state up* from `MyButton` into `MyApp`: + +```js {2-6,18} +export default function MyApp() { + const [count, setCount] = useState(0); + + function handleClick() { + setCount(count + 1); + } + + return ( +
    +

    Counters that update separately

    + + +
    + ); +} + +function MyButton() { + // ... we're moving code from here ... +} + +``` + +Then, *pass the state down* from `MyApp` to each `MyButton`, together with the shared click handler. You can pass information to `MyButton` using the JSX curly braces, just like you previously did with built-in tags like ``: + +```js {11-12} +export default function MyApp() { + const [count, setCount] = useState(0); + + function handleClick() { + setCount(count + 1); + } + + return ( +
    +

    Counters that update together

    + + +
    + ); +} +``` + +The information you pass down like this is called _props_. Now the `MyApp` component contains the `count` state and the `handleClick` event handler, and *passes both of them down as props* to each of the buttons. + +Finally, change `MyButton` to *read* the props you have passed from its parent component: + +```js {1,3} +function MyButton({ count, onClick }) { + return ( + + ); +} +``` + +When you click the button, the `onClick` handler fires. Each button's `onClick` prop was set to the `handleClick` function inside `MyApp`, so the code inside of it runs. That code calls `setCount(count + 1)`, incrementing the `count` state variable. The new `count` value is passed as a prop to each button, so they all show the new value. This is called "lifting state up". By moving state up, you've shared it between components. + + + +```js +import { useState } from 'react'; + +export default function MyApp() { + const [count, setCount] = useState(0); + + function handleClick() { + setCount(count + 1); + } + + return ( +
    +

    Counters that update together

    + + +
    + ); +} + +function MyButton({ count, onClick }) { + return ( + + ); +} +``` + +```css +button { + display: block; + margin-bottom: 5px; +} +``` + +
    + +## Next Steps {/*next-steps*/} + +By now, you know the basics of how to write React code! + +Check out the [Tutorial](/learn/tutorial-tic-tac-toe) to put them into practice and build your first mini-app with React. + +--- +title: "Tutorial: Tic-Tac-Toe" +url: "https://react.dev/learn/tutorial-tic-tac-toe" +--- + + + +You will build a small tic-tac-toe game during this tutorial. This tutorial does not assume any existing React knowledge. The techniques you'll learn in the tutorial are fundamental to building any React app, and fully understanding it will give you a deep understanding of React. + + + + + +This tutorial is designed for people who prefer to **learn by doing** and want to quickly try making something tangible. If you prefer learning each concept step by step, start with [Describing the UI.](/learn/describing-the-ui) + + + +The tutorial is divided into several sections: + +- [Setup for the tutorial](#setup-for-the-tutorial) will give you **a starting point** to follow the tutorial. +- [Overview](#overview) will teach you **the fundamentals** of React: components, props, and state. +- [Completing the game](#completing-the-game) will teach you **the most common techniques** in React development. +- [Adding time travel](#adding-time-travel) will give you **a deeper insight** into the unique strengths of React. + +### What are you building? {/*what-are-you-building*/} + +In this tutorial, you'll build an interactive tic-tac-toe game with React. + +You can see what it will look like when you're finished here: + + + +```js src/App.js +import { useState } from 'react'; + +function Square({ value, onSquareClick }) { + return ( + + ); +} + +function Board({ xIsNext, squares, onPlay }) { + function handleClick(i) { + if (calculateWinner(squares) || squares[i]) { + return; + } + const nextSquares = squares.slice(); + if (xIsNext) { + nextSquares[i] = 'X'; + } else { + nextSquares[i] = 'O'; + } + onPlay(nextSquares); + } + + const winner = calculateWinner(squares); + let status; + if (winner) { + status = 'Winner: ' + winner; + } else { + status = 'Next player: ' + (xIsNext ? 'X' : 'O'); + } + + return ( + <> +
    {status}
    +
    + handleClick(0)} /> + handleClick(1)} /> + handleClick(2)} /> +
    +
    + handleClick(3)} /> + handleClick(4)} /> + handleClick(5)} /> +
    +
    + handleClick(6)} /> + handleClick(7)} /> + handleClick(8)} /> +
    + + ); +} + +export default function Game() { + const [history, setHistory] = useState([Array(9).fill(null)]); + const [currentMove, setCurrentMove] = useState(0); + const xIsNext = currentMove % 2 === 0; + const currentSquares = history[currentMove]; + + function handlePlay(nextSquares) { + const nextHistory = [...history.slice(0, currentMove + 1), nextSquares]; + setHistory(nextHistory); + setCurrentMove(nextHistory.length - 1); + } + + function jumpTo(nextMove) { + setCurrentMove(nextMove); + } + + const moves = history.map((squares, move) => { + let description; + if (move > 0) { + description = 'Go to move #' + move; + } else { + description = 'Go to game start'; + } + return ( +
  • + +
  • + ); + }); + + return ( +
    +
    + +
    +
    +
      {moves}
    +
    +
    + ); +} + +function calculateWinner(squares) { + const lines = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6], + ]; + for (let i = 0; i < lines.length; i++) { + const [a, b, c] = lines[i]; + if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { + return squares[a]; + } + } + return null; +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + +
    + +If the code doesn't make sense to you yet, or if you are unfamiliar with the code's syntax, don't worry! The goal of this tutorial is to help you understand React and its syntax. + +We recommend that you check out the tic-tac-toe game above before continuing with the tutorial. One of the features that you'll notice is that there is a numbered list to the right of the game's board. This list gives you a history of all of the moves that have occurred in the game, and it is updated as the game progresses. + +Once you've played around with the finished tic-tac-toe game, keep scrolling. You'll start with a simpler template in this tutorial. Our next step is to set you up so that you can start building the game. + +## Setup for the tutorial {/*setup-for-the-tutorial*/} + +In the live code editor below, click **Fork** in the top-right corner to open the editor in a new tab using the website CodeSandbox. CodeSandbox lets you write code in your browser and preview how your users will see the app you've created. The new tab should display an empty square and the starter code for this tutorial. + + + +```js src/App.js +export default function Square() { + return ; +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + + + + + +You can also follow this tutorial using your local development environment. To do this, you need to: + +1. Install [Node.js](https://nodejs.org/en/) +1. In the CodeSandbox tab you opened earlier, press the top-left corner button to open the menu, and then choose **Download Sandbox** in that menu to download an archive of the files locally +1. Unzip the archive, then open a terminal and `cd` to the directory you unzipped +1. Install the dependencies with `npm install` +1. Run `npm start` to start a local server and follow the prompts to view the code running in a browser + +If you get stuck, don't let this stop you! Follow along online instead and try a local setup again later. + + + +## Overview {/*overview*/} + +Now that you're set up, let's get an overview of React! + +### Inspecting the starter code {/*inspecting-the-starter-code*/} + +In CodeSandbox you'll see three main sections: + +![CodeSandbox with starter code](../images/tutorial/react-starter-code-codesandbox.png) + +1. The _Files_ section with a list of files like `App.js`, `index.js`, `styles.css` in `src` folder and a folder called `public` +1. The _code editor_ where you'll see the source code of your selected file +1. The _browser_ section where you'll see how the code you've written will be displayed + +The `App.js` file should be selected in the _Files_ section. The contents of that file in the _code editor_ should be: + +```jsx +export default function Square() { + return ; +} +``` + +The _browser_ section should be displaying a square with an X in it like this: + +![x-filled square](../images/tutorial/x-filled-square.png) + +Now let's have a look at the files in the starter code. + +#### `App.js` {/*appjs*/} + +The code in `App.js` creates a _component_. In React, a component is a piece of reusable code that represents a part of a user interface. Components are used to render, manage, and update the UI elements in your application. Let's look at the component line by line to see what's going on: + +```js {1} +export default function Square() { + return ; +} +``` + +The first line defines a function called `Square`. The `export` JavaScript keyword makes this function accessible outside of this file. The `default` keyword tells other files using your code that it's the main function in your file. + +```js {2} +export default function Square() { + return ; +} +``` + +The second line returns a button. The `return` JavaScript keyword means whatever comes after is returned as a value to the caller of the function. `` closes the JSX element to indicate that any following content shouldn't be placed inside the button. + +#### `styles.css` {/*stylescss*/} + +Click on the file labeled `styles.css` in the _Files_ section of CodeSandbox. This file defines the styles for your React app. The first two _CSS selectors_ (`*` and `body`) define the style of large parts of your app while the `.square` selector defines the style of any component where the `className` property is set to `square`. In your code, that would match the button from your Square component in the `App.js` file. + +#### `index.js` {/*indexjs*/} + +Click on the file labeled `index.js` in the _Files_ section of CodeSandbox. You won't be editing this file during the tutorial but it is the bridge between the component you created in the `App.js` file and the web browser. + +```jsx +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import './styles.css'; + +import App from './App'; +``` + +Lines 1-5 bring all the necessary pieces together: + +* React +* React's library to talk to web browsers (React DOM) +* the styles for your components +* the component you created in `App.js`. + +The remainder of the file brings all the pieces together and injects the final product into `index.html` in the `public` folder. + +### Building the board {/*building-the-board*/} + +Let's get back to `App.js`. This is where you'll spend the rest of the tutorial. + +Currently the board is only a single square, but you need nine! If you just try and copy paste your square to make two squares like this: + +```js {2} +export default function Square() { + return ; +} +``` + +You'll get this error: + + + +/src/App.js: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX Fragment `<>...`? + + + +React components need to return a single JSX element and not multiple adjacent JSX elements like two buttons. To fix this you can use *Fragments* (`<>` and ``) to wrap multiple adjacent JSX elements like this: + +```js {3-6} +export default function Square() { + return ( + <> + + + + ); +} +``` + +Now you should see: + +![two x-filled squares](../images/tutorial/two-x-filled-squares.png) + +Great! Now you just need to copy-paste a few times to add nine squares and... + +![nine x-filled squares in a line](../images/tutorial/nine-x-filled-squares.png) + +Oh no! The squares are all in a single line, not in a grid like you need for our board. To fix this you'll need to group your squares into rows with `div`s and add some CSS classes. While you're at it, you'll give each square a number to make sure you know where each square is displayed. + +In the `App.js` file, update the `Square` component to look like this: + +```js {3-19} +export default function Square() { + return ( + <> +
    + + + +
    +
    + + + +
    +
    + + + +
    + + ); +} +``` + +The CSS defined in `styles.css` styles the divs with the `className` of `board-row`. Now that you've grouped your components into rows with the styled `div`s you have your tic-tac-toe board: + +![tic-tac-toe board filled with numbers 1 through 9](../images/tutorial/number-filled-board.png) + +But you now have a problem. Your component named `Square`, really isn't a square anymore. Let's fix that by changing the name to `Board`: + +```js {1} +export default function Board() { + //... +} +``` + +At this point your code should look something like this: + + + +```js +export default function Board() { + return ( + <> +
    + + + +
    +
    + + + +
    +
    + + + +
    + + ); +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + +
    + + + +Psssst... That's a lot to type! It's okay to copy and paste code from this page. However, if you're up for a little challenge, we recommend only copying code that you've manually typed at least once yourself. + + + +### Passing data through props {/*passing-data-through-props*/} + +Next, you'll want to change the value of a square from empty to "X" when the user clicks on the square. With how you've built the board so far you would need to copy-paste the code that updates the square nine times (once for each square you have)! Instead of copy-pasting, React's component architecture allows you to create a reusable component to avoid messy, duplicated code. + +First, you are going to copy the line defining your first square (``) from your `Board` component into a new `Square` component: + +```js {1-3} +function Square() { + return ; +} + +export default function Board() { + // ... +} +``` + +Then you'll update the Board component to render that `Square` component using JSX syntax: + +```js {5-19} +// ... +export default function Board() { + return ( + <> +
    + + + +
    +
    + + + +
    +
    + + + +
    + + ); +} +``` + +Note how unlike the browser `div`s, your own components `Board` and `Square` must start with a capital letter. + +Let's take a look: + +![one-filled board](../images/tutorial/board-filled-with-ones.png) + +Oh no! You lost the numbered squares you had before. Now each square says "1". To fix this, you will use *props* to pass the value each square should have from the parent component (`Board`) to its child (`Square`). + +Update the `Square` component to read the `value` prop that you'll pass from the `Board`: + +```js {1} +function Square({ value }) { + return ; +} +``` + +`function Square({ value })` indicates the Square component can be passed a prop called `value`. + +Now you want to display that `value` instead of `1` inside every square. Try doing it like this: + +```js {2} +function Square({ value }) { + return ; +} +``` + +Oops, this is not what you wanted: + +![value-filled board](../images/tutorial/board-filled-with-value.png) + +You wanted to render the JavaScript variable called `value` from your component, not the word "value". To "escape into JavaScript" from JSX, you need curly braces. Add curly braces around `value` in JSX like so: + +```js {2} +function Square({ value }) { + return ; +} +``` + +For now, you should see an empty board: + +![empty board](../images/tutorial/empty-board.png) + +This is because the `Board` component hasn't passed the `value` prop to each `Square` component it renders yet. To fix it you'll add the `value` prop to each `Square` component rendered by the `Board` component: + +```js {5-7,10-12,15-17} +export default function Board() { + return ( + <> +
    + + + +
    +
    + + + +
    +
    + + + +
    + + ); +} +``` + +Now you should see a grid of numbers again: + +![tic-tac-toe board filled with numbers 1 through 9](../images/tutorial/number-filled-board.png) + +Your updated code should look like this: + + + +```js src/App.js +function Square({ value }) { + return ; +} + +export default function Board() { + return ( + <> +
    + + + +
    +
    + + + +
    +
    + + + +
    + + ); +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + +
    + +### Making an interactive component {/*making-an-interactive-component*/} + +Let's fill the `Square` component with an `X` when you click it. Declare a function called `handleClick` inside of the `Square`. Then, add `onClick` to the props of the button JSX element returned from the `Square`: + +```js {2-4,9} +function Square({ value }) { + function handleClick() { + console.log('clicked!'); + } + + return ( + + ); +} +``` + +If you click on a square now, you should see a log saying `"clicked!"` in the _Console_ tab at the bottom of the _Browser_ section in CodeSandbox. Clicking the square more than once will log `"clicked!"` again. Repeated console logs with the same message will not create more lines in the console. Instead, you will see an incrementing counter next to your first `"clicked!"` log. + + + +If you are following this tutorial using your local development environment, you need to open your browser's Console. For example, if you use the Chrome browser, you can view the Console with the keyboard shortcut **Shift + Ctrl + J** (on Windows/Linux) or **Option + ⌘ + J** (on macOS). + + + +As a next step, you want the Square component to "remember" that it got clicked, and fill it with an "X" mark. To "remember" things, components use *state*. + +React provides a special function called `useState` that you can call from your component to let it "remember" things. Let's store the current value of the `Square` in state, and change it when the `Square` is clicked. + +Import `useState` at the top of the file. Remove the `value` prop from the `Square` component. Instead, add a new line at the start of the `Square` that calls `useState`. Have it return a state variable called `value`: + +```js {1,3,4} +import { useState } from 'react'; + +function Square() { + const [value, setValue] = useState(null); + + function handleClick() { + //... +``` + +`value` stores the value and `setValue` is a function that can be used to change the value. The `null` passed to `useState` is used as the initial value for this state variable, so `value` here starts off equal to `null`. + +Since the `Square` component no longer accepts props anymore, you'll remove the `value` prop from all nine of the Square components created by the Board component: + +```js {6-8,11-13,16-18} +// ... +export default function Board() { + return ( + <> +
    + + + +
    +
    + + + +
    +
    + + + +
    + + ); +} +``` + +Now you'll change `Square` to display an "X" when clicked. Replace the `console.log("clicked!");` event handler with `setValue('X');`. Now your `Square` component looks like this: + +```js {5} +function Square() { + const [value, setValue] = useState(null); + + function handleClick() { + setValue('X'); + } + + return ( + + ); +} +``` + +By calling this `set` function from an `onClick` handler, you're telling React to re-render that `Square` whenever its ` + ); +} + +export default function Board() { + return ( + <> +
    + + + +
    +
    + + + +
    +
    + + + +
    + + ); +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + + + +### React Developer Tools {/*react-developer-tools*/} + +React DevTools let you check the props and the state of your React components. You can find the React DevTools tab at the bottom of the _browser_ section in CodeSandbox: + +![React DevTools in CodeSandbox](../images/tutorial/codesandbox-devtools.png) + +To inspect a particular component on the screen, use the button in the top left corner of React DevTools: + +![Selecting components on the page with React DevTools](../images/tutorial/devtools-select.gif) + + + +For local development, React DevTools is available as a [Chrome](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en), [Firefox](https://addons.mozilla.org/en-US/firefox/addon/react-devtools/), and [Edge](https://microsoftedge.microsoft.com/addons/detail/react-developer-tools/gpphkfbcpidddadnkolkpfckpihlkkil) browser extension. Install it, and the *Components* tab will appear in your browser Developer Tools for sites using React. + + + +## Completing the game {/*completing-the-game*/} + +By this point, you have all the basic building blocks for your tic-tac-toe game. To have a complete game, you now need to alternate placing "X"s and "O"s on the board, and you need a way to determine a winner. + +### Lifting state up {/*lifting-state-up*/} + +Currently, each `Square` component maintains a part of the game's state. To check for a winner in a tic-tac-toe game, the `Board` would need to somehow know the state of each of the 9 `Square` components. + +How would you approach that? At first, you might guess that the `Board` needs to "ask" each `Square` for that `Square`'s state. Although this approach is technically possible in React, we discourage it because the code becomes difficult to understand, susceptible to bugs, and hard to refactor. Instead, the best approach is to store the game's state in the parent `Board` component instead of in each `Square`. The `Board` component can tell each `Square` what to display by passing a prop, like you did when you passed a number to each Square. + +**To collect data from multiple children, or to have two child components communicate with each other, declare the shared state in their parent component instead. The parent component can pass that state back down to the children via props. This keeps the child components in sync with each other and with their parent.** + +Lifting state into a parent component is common when React components are refactored. + +Let's take this opportunity to try it out. Edit the `Board` component so that it declares a state variable named `squares` that defaults to an array of 9 nulls corresponding to the 9 squares: + +```js {3} +// ... +export default function Board() { + const [squares, setSquares] = useState(Array(9).fill(null)); + return ( + // ... + ); +} +``` + +`Array(9).fill(null)` creates an array with nine elements and sets each of them to `null`. The `useState()` call around it declares a `squares` state variable that's initially set to that array. Each entry in the array corresponds to the value of a square. When you fill the board in later, the `squares` array will look like this: + +```jsx +['O', null, 'X', 'X', 'X', 'O', 'O', null, null] +``` + +Now your `Board` component needs to pass the `value` prop down to each `Square` that it renders: + +```js {6-8,11-13,16-18} +export default function Board() { + const [squares, setSquares] = useState(Array(9).fill(null)); + return ( + <> +
    + + + +
    +
    + + + +
    +
    + + + +
    + + ); +} +``` + +Next, you'll edit the `Square` component to receive the `value` prop from the Board component. This will require removing the Square component's own stateful tracking of `value` and the button's `onClick` prop: + +```js {1,2} +function Square({value}) { + return ; +} +``` + +At this point you should see an empty tic-tac-toe board: + +![empty board](../images/tutorial/empty-board.png) + +And your code should look like this: + + + +```js src/App.js +import { useState } from 'react'; + +function Square({ value }) { + return ; +} + +export default function Board() { + const [squares, setSquares] = useState(Array(9).fill(null)); + return ( + <> +
    + + + +
    +
    + + + +
    +
    + + + +
    + + ); +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + +
    + +Each Square will now receive a `value` prop that will either be `'X'`, `'O'`, or `null` for empty squares. + +Next, you need to change what happens when a `Square` is clicked. The `Board` component now maintains which squares are filled. You'll need to create a way for the `Square` to update the `Board`'s state. Since state is private to a component that defines it, you cannot update the `Board`'s state directly from `Square`. + +Instead, you'll pass down a function from the `Board` component to the `Square` component, and you'll have `Square` call that function when a square is clicked. You'll start with the function that the `Square` component will call when it is clicked. You'll call that function `onSquareClick`: + +```js {3} +function Square({ value }) { + return ( + + ); +} +``` + +Next, you'll add the `onSquareClick` function to the `Square` component's props: + +```js {1} +function Square({ value, onSquareClick }) { + return ( + + ); +} +``` + +Now you'll connect the `onSquareClick` prop to a function in the `Board` component that you'll name `handleClick`. To connect `onSquareClick` to `handleClick` you'll pass a function to the `onSquareClick` prop of the first `Square` component: + +```js {7} +export default function Board() { + const [squares, setSquares] = useState(Array(9).fill(null)); + + return ( + <> +
    + + //... + ); +} +``` + +Lastly, you will define the `handleClick` function inside the Board component to update the `squares` array holding your board's state: + +```js {4-8} +export default function Board() { + const [squares, setSquares] = useState(Array(9).fill(null)); + + function handleClick() { + const nextSquares = squares.slice(); + nextSquares[0] = "X"; + setSquares(nextSquares); + } + + return ( + // ... + ) +} +``` + +The `handleClick` function creates a copy of the `squares` array (`nextSquares`) with the JavaScript `slice()` Array method. Then, `handleClick` updates the `nextSquares` array to add `X` to the first (`[0]` index) square. + +Calling the `setSquares` function lets React know the state of the component has changed. This will trigger a re-render of the components that use the `squares` state (`Board`) as well as its child components (the `Square` components that make up the board). + + + +JavaScript supports [closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures) which means an inner function (e.g. `handleClick`) has access to variables and functions defined in an outer function (e.g. `Board`). The `handleClick` function can read the `squares` state and call the `setSquares` method because they are both defined inside of the `Board` function. + + + +Now you can add X's to the board... but only to the upper left square. Your `handleClick` function is hardcoded to update the index for the upper left square (`0`). Let's update `handleClick` to be able to update any square. Add an argument `i` to the `handleClick` function that takes the index of the square to update: + +```js {4,6} +export default function Board() { + const [squares, setSquares] = useState(Array(9).fill(null)); + + function handleClick(i) { + const nextSquares = squares.slice(); + nextSquares[i] = "X"; + setSquares(nextSquares); + } + + return ( + // ... + ) +} +``` + +Next, you will need to pass that `i` to `handleClick`. You could try to set the `onSquareClick` prop of square to be `handleClick(0)` directly in the JSX like this, but it won't work: + +```jsx + +``` + +Here is why this doesn't work. The `handleClick(0)` call will be a part of rendering the board component. Because `handleClick(0)` alters the state of the board component by calling `setSquares`, your entire board component will be re-rendered again. But this runs `handleClick(0)` again, leading to an infinite loop: + + + +Too many re-renders. React limits the number of renders to prevent an infinite loop. + + + +Why didn't this problem happen earlier? + +When you were passing `onSquareClick={handleClick}`, you were passing the `handleClick` function down as a prop. You were not calling it! But now you are *calling* that function right away--notice the parentheses in `handleClick(0)`--and that's why it runs too early. You don't *want* to call `handleClick` until the user clicks! + +You could fix this by creating a function like `handleFirstSquareClick` that calls `handleClick(0)`, a function like `handleSecondSquareClick` that calls `handleClick(1)`, and so on. You would pass (rather than call) these functions down as props like `onSquareClick={handleFirstSquareClick}`. This would solve the infinite loop. + +However, defining nine different functions and giving each of them a name is too verbose. Instead, let's do this: + +```js {6} +export default function Board() { + // ... + return ( + <> +
    + handleClick(0)} /> + // ... + ); +} +``` + +Notice the new `() =>` syntax. Here, `() => handleClick(0)` is an *arrow function,* which is a shorter way to define functions. When the square is clicked, the code after the `=>` "arrow" will run, calling `handleClick(0)`. + +Now you need to update the other eight squares to call `handleClick` from the arrow functions you pass. Make sure that the argument for each call of the `handleClick` corresponds to the index of the correct square: + +```js {6-8,11-13,16-18} +export default function Board() { + // ... + return ( + <> +
    + handleClick(0)} /> + handleClick(1)} /> + handleClick(2)} /> +
    +
    + handleClick(3)} /> + handleClick(4)} /> + handleClick(5)} /> +
    +
    + handleClick(6)} /> + handleClick(7)} /> + handleClick(8)} /> +
    + + ); +}; +``` + +Now you can again add X's to any square on the board by clicking on them: + +![filling the board with X](../images/tutorial/tictac-adding-x-s.gif) + +But this time all the state management is handled by the `Board` component! + +This is what your code should look like: + + + +```js src/App.js +import { useState } from 'react'; + +function Square({ value, onSquareClick }) { + return ( + + ); +} + +export default function Board() { + const [squares, setSquares] = useState(Array(9).fill(null)); + + function handleClick(i) { + const nextSquares = squares.slice(); + nextSquares[i] = 'X'; + setSquares(nextSquares); + } + + return ( + <> +
    + handleClick(0)} /> + handleClick(1)} /> + handleClick(2)} /> +
    +
    + handleClick(3)} /> + handleClick(4)} /> + handleClick(5)} /> +
    +
    + handleClick(6)} /> + handleClick(7)} /> + handleClick(8)} /> +
    + + ); +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + +
    + +Now that your state handling is in the `Board` component, the parent `Board` component passes props to the child `Square` components so that they can be displayed correctly. When clicking on a `Square`, the child `Square` component now asks the parent `Board` component to update the state of the board. When the `Board`'s state changes, both the `Board` component and every child `Square` re-renders automatically. Keeping the state of all squares in the `Board` component will allow it to determine the winner in the future. + +Let's recap what happens when a user clicks the top left square on your board to add an `X` to it: + +1. Clicking on the upper left square runs the function that the `button` received as its `onClick` prop from the `Square`. The `Square` component received that function as its `onSquareClick` prop from the `Board`. The `Board` component defined that function directly in the JSX. It calls `handleClick` with an argument of `0`. +1. `handleClick` uses the argument (`0`) to update the first element of the `squares` array from `null` to `X`. +1. The `squares` state of the `Board` component was updated, so the `Board` and all of its children re-render. This causes the `value` prop of the `Square` component with index `0` to change from `null` to `X`. + +In the end the user sees that the upper left square has changed from empty to having an `X` after clicking it. + + + +The DOM ` + ); +} + +export default function Board() { + const [xIsNext, setXIsNext] = useState(true); + const [squares, setSquares] = useState(Array(9).fill(null)); + + function handleClick(i) { + if (squares[i]) { + return; + } + const nextSquares = squares.slice(); + if (xIsNext) { + nextSquares[i] = 'X'; + } else { + nextSquares[i] = 'O'; + } + setSquares(nextSquares); + setXIsNext(!xIsNext); + } + + return ( + <> +
    + handleClick(0)} /> + handleClick(1)} /> + handleClick(2)} /> +
    +
    + handleClick(3)} /> + handleClick(4)} /> + handleClick(5)} /> +
    +
    + handleClick(6)} /> + handleClick(7)} /> + handleClick(8)} /> +
    + + ); +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + + + +### Declaring a winner {/*declaring-a-winner*/} + +Now that the players can take turns, you'll want to show when the game is won and there are no more turns to make. To do this you'll add a helper function called `calculateWinner` that takes an array of 9 squares, checks for a winner and returns `'X'`, `'O'`, or `null` as appropriate. Don't worry too much about the `calculateWinner` function; it's not specific to React: + +```js src/App.js +export default function Board() { + //... +} + +function calculateWinner(squares) { + const lines = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6] + ]; + for (let i = 0; i < lines.length; i++) { + const [a, b, c] = lines[i]; + if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { + return squares[a]; + } + } + return null; +} +``` + + + +It does not matter whether you define `calculateWinner` before or after the `Board`. Let's put it at the end so that you don't have to scroll past it every time you edit your components. + + + +You will call `calculateWinner(squares)` in the `Board` component's `handleClick` function to check if a player has won. You can perform this check at the same time you check if a user has clicked a square that already has an `X` or an `O`. We'd like to return early in both cases: + +```js {2} +function handleClick(i) { + if (squares[i] || calculateWinner(squares)) { + return; + } + const nextSquares = squares.slice(); + //... +} +``` + +To let the players know when the game is over, you can display text such as "Winner: X" or "Winner: O". To do that you'll add a `status` section to the `Board` component. The status will display the winner if the game is over and if the game is ongoing you'll display which player's turn is next: + +```js {3-9,13} +export default function Board() { + // ... + const winner = calculateWinner(squares); + let status; + if (winner) { + status = "Winner: " + winner; + } else { + status = "Next player: " + (xIsNext ? "X" : "O"); + } + + return ( + <> +
    {status}
    +
    + // ... + ) +} +``` + +Congratulations! You now have a working tic-tac-toe game. And you've just learned the basics of React too. So _you_ are the real winner here. Here is what the code should look like: + + + +```js src/App.js +import { useState } from 'react'; + +function Square({value, onSquareClick}) { + return ( + + ); +} + +export default function Board() { + const [xIsNext, setXIsNext] = useState(true); + const [squares, setSquares] = useState(Array(9).fill(null)); + + function handleClick(i) { + if (calculateWinner(squares) || squares[i]) { + return; + } + const nextSquares = squares.slice(); + if (xIsNext) { + nextSquares[i] = 'X'; + } else { + nextSquares[i] = 'O'; + } + setSquares(nextSquares); + setXIsNext(!xIsNext); + } + + const winner = calculateWinner(squares); + let status; + if (winner) { + status = 'Winner: ' + winner; + } else { + status = 'Next player: ' + (xIsNext ? 'X' : 'O'); + } + + return ( + <> +
    {status}
    +
    + handleClick(0)} /> + handleClick(1)} /> + handleClick(2)} /> +
    +
    + handleClick(3)} /> + handleClick(4)} /> + handleClick(5)} /> +
    +
    + handleClick(6)} /> + handleClick(7)} /> + handleClick(8)} /> +
    + + ); +} + +function calculateWinner(squares) { + const lines = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6], + ]; + for (let i = 0; i < lines.length; i++) { + const [a, b, c] = lines[i]; + if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { + return squares[a]; + } + } + return null; +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + +
    + +## Adding time travel {/*adding-time-travel*/} + +As a final exercise, let's make it possible to "go back in time" to the previous moves in the game. + +### Storing a history of moves {/*storing-a-history-of-moves*/} + +If you mutated the `squares` array, implementing time travel would be very difficult. + +However, you used `slice()` to create a new copy of the `squares` array after every move, and treated it as immutable. This will allow you to store every past version of the `squares` array, and navigate between the turns that have already happened. + +You'll store the past `squares` arrays in another array called `history`, which you'll store as a new state variable. The `history` array represents all board states, from the first to the last move, and has a shape like this: + +```jsx +[ + // Before first move + [null, null, null, null, null, null, null, null, null], + // After first move + [null, null, null, null, 'X', null, null, null, null], + // After second move + [null, null, null, null, 'X', null, null, null, 'O'], + // ... +] +``` + +### Lifting state up, again {/*lifting-state-up-again*/} + +You will now write a new top-level component called `Game` to display a list of past moves. That's where you will place the `history` state that contains the entire game history. + +Placing the `history` state into the `Game` component will let you remove the `squares` state from its child `Board` component. Just like you "lifted state up" from the `Square` component into the `Board` component, you will now lift it up from the `Board` into the top-level `Game` component. This gives the `Game` component full control over the `Board`'s data and lets it instruct the `Board` to render previous turns from the `history`. + +First, add a `Game` component with `export default`. Have it render the `Board` component and some markup: + +```js {1,5-16} +function Board() { + // ... +} + +export default function Game() { + return ( +
    +
    + +
    +
    +
      {/*TODO*/}
    +
    +
    + ); +} +``` + +Note that you are removing the `export default` keywords before the `function Board() {` declaration and adding them before the `function Game() {` declaration. This tells your `index.js` file to use the `Game` component as the top-level component instead of your `Board` component. The additional `div`s returned by the `Game` component are making room for the game information you'll add to the board later. + +Add some state to the `Game` component to track which player is next and the history of moves: + +```js {2-3} +export default function Game() { + const [xIsNext, setXIsNext] = useState(true); + const [history, setHistory] = useState([Array(9).fill(null)]); + // ... +``` + +Notice how `[Array(9).fill(null)]` is an array with a single item, which itself is an array of 9 `null`s. + +To render the squares for the current move, you'll want to read the last squares array from the `history`. You don't need `useState` for this--you already have enough information to calculate it during rendering: + +```js {4} +export default function Game() { + const [xIsNext, setXIsNext] = useState(true); + const [history, setHistory] = useState([Array(9).fill(null)]); + const currentSquares = history[history.length - 1]; + // ... +``` + +Next, create a `handlePlay` function inside the `Game` component that will be called by the `Board` component to update the game. Pass `xIsNext`, `currentSquares` and `handlePlay` as props to the `Board` component: + +```js {6-8,13} +export default function Game() { + const [xIsNext, setXIsNext] = useState(true); + const [history, setHistory] = useState([Array(9).fill(null)]); + const currentSquares = history[history.length - 1]; + + function handlePlay(nextSquares) { + // TODO + } + + return ( +
    +
    + + //... + ) +} +``` + +Let's make the `Board` component fully controlled by the props it receives. Change the `Board` component to take three props: `xIsNext`, `squares`, and a new `onPlay` function that `Board` can call with the updated squares array when a player makes a move. Next, remove the first two lines of the `Board` function that call `useState`: + +```js {1} +function Board({ xIsNext, squares, onPlay }) { + function handleClick(i) { + //... + } + // ... +} +``` + +Now replace the `setSquares` and `setXIsNext` calls in `handleClick` in the `Board` component with a single call to your new `onPlay` function so the `Game` component can update the `Board` when the user clicks a square: + +```js {12} +function Board({ xIsNext, squares, onPlay }) { + function handleClick(i) { + if (calculateWinner(squares) || squares[i]) { + return; + } + const nextSquares = squares.slice(); + if (xIsNext) { + nextSquares[i] = "X"; + } else { + nextSquares[i] = "O"; + } + onPlay(nextSquares); + } + //... +} +``` + +The `Board` component is fully controlled by the props passed to it by the `Game` component. You need to implement the `handlePlay` function in the `Game` component to get the game working again. + +What should `handlePlay` do when called? Remember that Board used to call `setSquares` with an updated array; now it passes the updated `squares` array to `onPlay`. + +The `handlePlay` function needs to update `Game`'s state to trigger a re-render, but you don't have a `setSquares` function that you can call any more--you're now using the `history` state variable to store this information. You'll want to update `history` by appending the updated `squares` array as a new history entry. You also want to toggle `xIsNext`, just as Board used to do: + +```js {4-5} +export default function Game() { + //... + function handlePlay(nextSquares) { + setHistory([...history, nextSquares]); + setXIsNext(!xIsNext); + } + //... +} +``` + +Here, `[...history, nextSquares]` creates a new array that contains all the items in `history`, followed by `nextSquares`. (You can read the `...history` [*spread syntax*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) as "enumerate all the items in `history`".) + +For example, if `history` is `[[null,null,null], ["X",null,null]]` and `nextSquares` is `["X",null,"O"]`, then the new `[...history, nextSquares]` array will be `[[null,null,null], ["X",null,null], ["X",null,"O"]]`. + +At this point, you've moved the state to live in the `Game` component, and the UI should be fully working, just as it was before the refactor. Here is what the code should look like at this point: + + + +```js src/App.js +import { useState } from 'react'; + +function Square({ value, onSquareClick }) { + return ( + + ); +} + +function Board({ xIsNext, squares, onPlay }) { + function handleClick(i) { + if (calculateWinner(squares) || squares[i]) { + return; + } + const nextSquares = squares.slice(); + if (xIsNext) { + nextSquares[i] = 'X'; + } else { + nextSquares[i] = 'O'; + } + onPlay(nextSquares); + } + + const winner = calculateWinner(squares); + let status; + if (winner) { + status = 'Winner: ' + winner; + } else { + status = 'Next player: ' + (xIsNext ? 'X' : 'O'); + } + + return ( + <> +
    {status}
    +
    + handleClick(0)} /> + handleClick(1)} /> + handleClick(2)} /> +
    +
    + handleClick(3)} /> + handleClick(4)} /> + handleClick(5)} /> +
    +
    + handleClick(6)} /> + handleClick(7)} /> + handleClick(8)} /> +
    + + ); +} + +export default function Game() { + const [xIsNext, setXIsNext] = useState(true); + const [history, setHistory] = useState([Array(9).fill(null)]); + const currentSquares = history[history.length - 1]; + + function handlePlay(nextSquares) { + setHistory([...history, nextSquares]); + setXIsNext(!xIsNext); + } + + return ( +
    +
    + +
    +
    +
      {/*TODO*/}
    +
    +
    + ); +} + +function calculateWinner(squares) { + const lines = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6], + ]; + for (let i = 0; i < lines.length; i++) { + const [a, b, c] = lines[i]; + if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { + return squares[a]; + } + } + return null; +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + +
    + +### Showing the past moves {/*showing-the-past-moves*/} + +Since you are recording the tic-tac-toe game's history, you can now display a list of past moves to the player. + +React elements like ` + + ); + }); + + return ( +
    +
    + +
    +
    +
      {moves}
    +
    +
    + ); +} +``` + +You can see what your code should look like below. Note that you should see an error in the developer tools console that says: + + +Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `Game`. + + +You'll fix this error in the next section. + + + +```js src/App.js +import { useState } from 'react'; + +function Square({ value, onSquareClick }) { + return ( + + ); +} + +function Board({ xIsNext, squares, onPlay }) { + function handleClick(i) { + if (calculateWinner(squares) || squares[i]) { + return; + } + const nextSquares = squares.slice(); + if (xIsNext) { + nextSquares[i] = 'X'; + } else { + nextSquares[i] = 'O'; + } + onPlay(nextSquares); + } + + const winner = calculateWinner(squares); + let status; + if (winner) { + status = 'Winner: ' + winner; + } else { + status = 'Next player: ' + (xIsNext ? 'X' : 'O'); + } + + return ( + <> +
    {status}
    +
    + handleClick(0)} /> + handleClick(1)} /> + handleClick(2)} /> +
    +
    + handleClick(3)} /> + handleClick(4)} /> + handleClick(5)} /> +
    +
    + handleClick(6)} /> + handleClick(7)} /> + handleClick(8)} /> +
    + + ); +} + +export default function Game() { + const [xIsNext, setXIsNext] = useState(true); + const [history, setHistory] = useState([Array(9).fill(null)]); + const currentSquares = history[history.length - 1]; + + function handlePlay(nextSquares) { + setHistory([...history, nextSquares]); + setXIsNext(!xIsNext); + } + + function jumpTo(nextMove) { + // TODO + } + + const moves = history.map((squares, move) => { + let description; + if (move > 0) { + description = 'Go to move #' + move; + } else { + description = 'Go to game start'; + } + return ( +
  • + +
  • + ); + }); + + return ( +
    +
    + +
    +
    +
      {moves}
    +
    +
    + ); +} + +function calculateWinner(squares) { + const lines = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6], + ]; + for (let i = 0; i < lines.length; i++) { + const [a, b, c] = lines[i]; + if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { + return squares[a]; + } + } + return null; +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} + +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + +
    + +As you iterate through the `history` array inside the function you passed to `map`, the `squares` argument goes through each element of `history`, and the `move` argument goes through each array index: `0`, `1`, `2`, …. (In most cases, you'd need the actual array elements, but to render a list of moves you will only need indexes.) + +For each move in the tic-tac-toe game's history, you create a list item `
  • ` which contains a button ` +
  • + ); +}); +``` + + + +```js src/App.js +import { useState } from 'react'; + +function Square({ value, onSquareClick }) { + return ( + + ); +} + +function Board({ xIsNext, squares, onPlay }) { + function handleClick(i) { + if (calculateWinner(squares) || squares[i]) { + return; + } + const nextSquares = squares.slice(); + if (xIsNext) { + nextSquares[i] = 'X'; + } else { + nextSquares[i] = 'O'; + } + onPlay(nextSquares); + } + + const winner = calculateWinner(squares); + let status; + if (winner) { + status = 'Winner: ' + winner; + } else { + status = 'Next player: ' + (xIsNext ? 'X' : 'O'); + } + + return ( + <> +
    {status}
    +
    + handleClick(0)} /> + handleClick(1)} /> + handleClick(2)} /> +
    +
    + handleClick(3)} /> + handleClick(4)} /> + handleClick(5)} /> +
    +
    + handleClick(6)} /> + handleClick(7)} /> + handleClick(8)} /> +
    + + ); +} + +export default function Game() { + const [xIsNext, setXIsNext] = useState(true); + const [history, setHistory] = useState([Array(9).fill(null)]); + const currentSquares = history[history.length - 1]; + + function handlePlay(nextSquares) { + setHistory([...history, nextSquares]); + setXIsNext(!xIsNext); + } + + function jumpTo(nextMove) { + // TODO + } + + const moves = history.map((squares, move) => { + let description; + if (move > 0) { + description = 'Go to move #' + move; + } else { + description = 'Go to game start'; + } + return ( +
  • + +
  • + ); + }); + + return ( +
    +
    + +
    +
    +
      {moves}
    +
    +
    + ); +} + +function calculateWinner(squares) { + const lines = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6], + ]; + for (let i = 0; i < lines.length; i++) { + const [a, b, c] = lines[i]; + if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { + return squares[a]; + } + } + return null; +} + +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} + +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + +
    + +Before you can implement `jumpTo`, you need the `Game` component to keep track of which step the user is currently viewing. To do this, define a new state variable called `currentMove`, defaulting to `0`: + +```js {4} +export default function Game() { + const [xIsNext, setXIsNext] = useState(true); + const [history, setHistory] = useState([Array(9).fill(null)]); + const [currentMove, setCurrentMove] = useState(0); + const currentSquares = history[history.length - 1]; + //... +} +``` + +Next, update the `jumpTo` function inside `Game` to update that `currentMove`. You'll also set `xIsNext` to `true` if the number that you're changing `currentMove` to is even. + +```js {4-5} +export default function Game() { + // ... + function jumpTo(nextMove) { + setCurrentMove(nextMove); + setXIsNext(nextMove % 2 === 0); + } + //... +} +``` + +You will now make two changes to the `Game`'s `handlePlay` function which is called when you click on a square. + +- If you "go back in time" and then make a new move from that point, you only want to keep the history up to that point. Instead of adding `nextSquares` after all items (`...` spread syntax) in `history`, you'll add it after all items in `history.slice(0, currentMove + 1)` so that you're only keeping that portion of the old history. +- Each time a move is made, you need to update `currentMove` to point to the latest history entry. + +```js {2-4} +function handlePlay(nextSquares) { + const nextHistory = [...history.slice(0, currentMove + 1), nextSquares]; + setHistory(nextHistory); + setCurrentMove(nextHistory.length - 1); + setXIsNext(!xIsNext); +} +``` + +Finally, you will modify the `Game` component to render the currently selected move, instead of always rendering the final move: + +```js {5} +export default function Game() { + const [xIsNext, setXIsNext] = useState(true); + const [history, setHistory] = useState([Array(9).fill(null)]); + const [currentMove, setCurrentMove] = useState(0); + const currentSquares = history[currentMove]; + + // ... +} +``` + +If you click on any step in the game's history, the tic-tac-toe board should immediately update to show what the board looked like after that step occurred. + + + +```js src/App.js +import { useState } from 'react'; + +function Square({value, onSquareClick}) { + return ( + + ); +} + +function Board({ xIsNext, squares, onPlay }) { + function handleClick(i) { + if (calculateWinner(squares) || squares[i]) { + return; + } + const nextSquares = squares.slice(); + if (xIsNext) { + nextSquares[i] = 'X'; + } else { + nextSquares[i] = 'O'; + } + onPlay(nextSquares); + } + + const winner = calculateWinner(squares); + let status; + if (winner) { + status = 'Winner: ' + winner; + } else { + status = 'Next player: ' + (xIsNext ? 'X' : 'O'); + } + + return ( + <> +
    {status}
    +
    + handleClick(0)} /> + handleClick(1)} /> + handleClick(2)} /> +
    +
    + handleClick(3)} /> + handleClick(4)} /> + handleClick(5)} /> +
    +
    + handleClick(6)} /> + handleClick(7)} /> + handleClick(8)} /> +
    + + ); +} + +export default function Game() { + const [xIsNext, setXIsNext] = useState(true); + const [history, setHistory] = useState([Array(9).fill(null)]); + const [currentMove, setCurrentMove] = useState(0); + const currentSquares = history[currentMove]; + + function handlePlay(nextSquares) { + const nextHistory = [...history.slice(0, currentMove + 1), nextSquares]; + setHistory(nextHistory); + setCurrentMove(nextHistory.length - 1); + setXIsNext(!xIsNext); + } + + function jumpTo(nextMove) { + setCurrentMove(nextMove); + setXIsNext(nextMove % 2 === 0); + } + + const moves = history.map((squares, move) => { + let description; + if (move > 0) { + description = 'Go to move #' + move; + } else { + description = 'Go to game start'; + } + return ( +
  • + +
  • + ); + }); + + return ( +
    +
    + +
    +
    +
      {moves}
    +
    +
    + ); +} + +function calculateWinner(squares) { + const lines = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6], + ]; + for (let i = 0; i < lines.length; i++) { + const [a, b, c] = lines[i]; + if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { + return squares[a]; + } + } + return null; +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + +
    + +### Final cleanup {/*final-cleanup*/} + +If you look at the code very closely, you may notice that `xIsNext === true` when `currentMove` is even and `xIsNext === false` when `currentMove` is odd. In other words, if you know the value of `currentMove`, then you can always figure out what `xIsNext` should be. + +There's no reason for you to store both of these in state. In fact, always try to avoid redundant state. Simplifying what you store in state reduces bugs and makes your code easier to understand. Change `Game` so that it doesn't store `xIsNext` as a separate state variable and instead figures it out based on the `currentMove`: + +```js {4,11,15} +export default function Game() { + const [history, setHistory] = useState([Array(9).fill(null)]); + const [currentMove, setCurrentMove] = useState(0); + const xIsNext = currentMove % 2 === 0; + const currentSquares = history[currentMove]; + + function handlePlay(nextSquares) { + const nextHistory = [...history.slice(0, currentMove + 1), nextSquares]; + setHistory(nextHistory); + setCurrentMove(nextHistory.length - 1); + } + + function jumpTo(nextMove) { + setCurrentMove(nextMove); + } + // ... +} +``` + +You no longer need the `xIsNext` state declaration or the calls to `setXIsNext`. Now, there's no chance for `xIsNext` to get out of sync with `currentMove`, even if you make a mistake while coding the components. + +### Wrapping up {/*wrapping-up*/} + +Congratulations! You've created a tic-tac-toe game that: + +- Lets you play tic-tac-toe, +- Indicates when a player has won the game, +- Stores a game's history as a game progresses, +- Allows players to review a game's history and see previous versions of a game's board. + +Nice work! We hope you now feel like you have a decent grasp of how React works. + +Check out the final result here: + + + +```js src/App.js +import { useState } from 'react'; + +function Square({ value, onSquareClick }) { + return ( + + ); +} + +function Board({ xIsNext, squares, onPlay }) { + function handleClick(i) { + if (calculateWinner(squares) || squares[i]) { + return; + } + const nextSquares = squares.slice(); + if (xIsNext) { + nextSquares[i] = 'X'; + } else { + nextSquares[i] = 'O'; + } + onPlay(nextSquares); + } + + const winner = calculateWinner(squares); + let status; + if (winner) { + status = 'Winner: ' + winner; + } else { + status = 'Next player: ' + (xIsNext ? 'X' : 'O'); + } + + return ( + <> +
    {status}
    +
    + handleClick(0)} /> + handleClick(1)} /> + handleClick(2)} /> +
    +
    + handleClick(3)} /> + handleClick(4)} /> + handleClick(5)} /> +
    +
    + handleClick(6)} /> + handleClick(7)} /> + handleClick(8)} /> +
    + + ); +} + +export default function Game() { + const [history, setHistory] = useState([Array(9).fill(null)]); + const [currentMove, setCurrentMove] = useState(0); + const xIsNext = currentMove % 2 === 0; + const currentSquares = history[currentMove]; + + function handlePlay(nextSquares) { + const nextHistory = [...history.slice(0, currentMove + 1), nextSquares]; + setHistory(nextHistory); + setCurrentMove(nextHistory.length - 1); + } + + function jumpTo(nextMove) { + setCurrentMove(nextMove); + } + + const moves = history.map((squares, move) => { + let description; + if (move > 0) { + description = 'Go to move #' + move; + } else { + description = 'Go to game start'; + } + return ( +
  • + +
  • + ); + }); + + return ( +
    +
    + +
    +
    +
      {moves}
    +
    +
    + ); +} + +function calculateWinner(squares) { + const lines = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6], + ]; + for (let i = 0; i < lines.length; i++) { + const [a, b, c] = lines[i]; + if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { + return squares[a]; + } + } + return null; +} +``` + +```css src/styles.css +* { + box-sizing: border-box; +} + +body { + font-family: sans-serif; + margin: 20px; + padding: 0; +} + +.square { + background: #fff; + border: 1px solid #999; + float: left; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; +} + +.board-row:after { + clear: both; + content: ''; + display: table; +} + +.status { + margin-bottom: 10px; +} +.game { + display: flex; + flex-direction: row; +} + +.game-info { + margin-left: 20px; +} +``` + +
    + +If you have extra time or want to practice your new React skills, here are some ideas for improvements that you could make to the tic-tac-toe game, listed in order of increasing difficulty: + +1. For the current move only, show "You are at move #..." instead of a button. +1. Rewrite `Board` to use two loops to make the squares instead of hardcoding them. +1. Add a toggle button that lets you sort the moves in either ascending or descending order. +1. When someone wins, highlight the three squares that caused the win (and when no one wins, display a message about the result being a draw). +1. Display the location for each move in the format (row, col) in the move history list. + +Throughout this tutorial, you've touched on React concepts including elements, components, props, and state. Now that you've seen how these concepts work when building a game, check out [Thinking in React](/learn/thinking-in-react) to see how the same React concepts work when building an app's UI. + +--- +title: "Thinking in React" +url: "https://react.dev/learn/thinking-in-react" +--- + + + +React can change how you think about the designs you look at and the apps you build. When you build a user interface with React, you will first break it apart into pieces called *components*. Then, you will describe the different visual states for each of your components. Finally, you will connect your components together so that the data flows through them. In this tutorial, we’ll guide you through the thought process of building a searchable product data table with React. + + + +## Start with the mockup {/*start-with-the-mockup*/} + +Imagine that you already have a JSON API and a mockup from a designer. + +The JSON API returns some data that looks like this: + +```json +[ + { category: "Fruits", price: "$1", stocked: true, name: "Apple" }, + { category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit" }, + { category: "Fruits", price: "$2", stocked: false, name: "Passionfruit" }, + { category: "Vegetables", price: "$2", stocked: true, name: "Spinach" }, + { category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin" }, + { category: "Vegetables", price: "$1", stocked: true, name: "Peas" } +] +``` + +The mockup looks like this: + + + +To implement a UI in React, you will usually follow the same five steps. + +## Step 1: Break the UI into a component hierarchy {/*step-1-break-the-ui-into-a-component-hierarchy*/} + +Start by drawing boxes around every component and subcomponent in the mockup and naming them. If you work with a designer, they may have already named these components in their design tool. Ask them! + +Depending on your background, you can think about splitting up a design into components in different ways: + +* **Programming**--use the same techniques for deciding if you should create a new function or object. One such technique is the [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns), that is, a component should ideally only be concerned with one thing. If it ends up growing, it should be decomposed into smaller subcomponents. +* **CSS**--consider what you would make class selectors for. (However, components are a bit less granular.) +* **Design**--consider how you would organize the design's layers. + +If your JSON is well-structured, you'll often find that it naturally maps to the component structure of your UI. That's because UI and data models often have the same information architecture--that is, the same shape. Separate your UI into components, where each component matches one piece of your data model. + +There are five components on this screen: + + + + + + + +1. `FilterableProductTable` (grey) contains the entire app. +2. `SearchBar` (blue) receives the user input. +3. `ProductTable` (lavender) displays and filters the list according to the user input. +4. `ProductCategoryRow` (green) displays a heading for each category. +5. `ProductRow` (yellow) displays a row for each product. + + + + + +If you look at `ProductTable` (lavender), you'll see that the table header (containing the "Name" and "Price" labels) isn't its own component. This is a matter of preference, and you could go either way. For this example, it is a part of `ProductTable` because it appears inside the `ProductTable`'s list. However, if this header grows to be complex (e.g., if you add sorting), you can move it into its own `ProductTableHeader` component. + +Now that you've identified the components in the mockup, arrange them into a hierarchy. Components that appear within another component in the mockup should appear as a child in the hierarchy: + +* `FilterableProductTable` + * `SearchBar` + * `ProductTable` + * `ProductCategoryRow` + * `ProductRow` + +## Step 2: Build a static version in React {/*step-2-build-a-static-version-in-react*/} + +Now that you have your component hierarchy, it's time to implement your app. The most straightforward approach is to build a version that renders the UI from your data model without adding any interactivity... yet! It's often easier to build the static version first and add interactivity later. Building a static version requires a lot of typing and no thinking, but adding interactivity requires a lot of thinking and not a lot of typing. + +To build a static version of your app that renders your data model, you'll want to build [components](/learn/your-first-component) that reuse other components and pass data using [props.](/learn/passing-props-to-a-component) Props are a way of passing data from parent to child. (If you're familiar with the concept of [state](/learn/state-a-components-memory), don't use state at all to build this static version. State is reserved only for interactivity, that is, data that changes over time. Since this is a static version of the app, you don't need it.) + +You can either build "top down" by starting with building the components higher up in the hierarchy (like `FilterableProductTable`) or "bottom up" by working from components lower down (like `ProductRow`). In simpler examples, it’s usually easier to go top-down, and on larger projects, it’s easier to go bottom-up. + + + +```jsx src/App.js +function ProductCategoryRow({ category }) { + return ( + + + {category} + + + ); +} + +function ProductRow({ product }) { + const name = product.stocked ? product.name : + + {product.name} + ; + + return ( + + {name} + {product.price} + + ); +} + +function ProductTable({ products }) { + const rows = []; + let lastCategory = null; + + products.forEach((product) => { + if (product.category !== lastCategory) { + rows.push( + + ); + } + rows.push( + + ); + lastCategory = product.category; + }); + + return ( + + + + + + + + {rows} +
    NamePrice
    + ); +} + +function SearchBar() { + return ( +
    + + +
    + ); +} + +function FilterableProductTable({ products }) { + return ( +
    + + +
    + ); +} + +const PRODUCTS = [ + {category: "Fruits", price: "$1", stocked: true, name: "Apple"}, + {category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"}, + {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"}, + {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"}, + {category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"}, + {category: "Vegetables", price: "$1", stocked: true, name: "Peas"} +]; + +export default function App() { + return ; +} +``` + +```css +body { + padding: 5px +} +label { + display: block; + margin-top: 5px; + margin-bottom: 5px; +} +th { + padding-top: 10px; +} +td { + padding: 2px; + padding-right: 40px; +} +``` + +
    + +(If this code looks intimidating, go through the [Quick Start](/learn/) first!) + +After building your components, you'll have a library of reusable components that render your data model. Because this is a static app, the components will only return JSX. The component at the top of the hierarchy (`FilterableProductTable`) will take your data model as a prop. This is called _one-way data flow_ because the data flows down from the top-level component to the ones at the bottom of the tree. + + + +At this point, you should not be using any state values. That’s for the next step! + + + +## Step 3: Find the minimal but complete representation of UI state {/*step-3-find-the-minimal-but-complete-representation-of-ui-state*/} + +To make the UI interactive, you need to let users change your underlying data model. You will use *state* for this. + +Think of state as the minimal set of changing data that your app needs to remember. The most important principle for structuring state is to keep it [DRY (Don't Repeat Yourself).](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) Figure out the absolute minimal representation of the state your application needs and compute everything else on-demand. For example, if you're building a shopping list, you can store the items as an array in state. If you want to also display the number of items in the list, don't store the number of items as another state value--instead, read the length of your array. + +Now think of all of the pieces of data in this example application: + +1. The original list of products +2. The search text the user has entered +3. The value of the checkbox +4. The filtered list of products + +Which of these are state? Identify the ones that are not: + +* Does it **remain unchanged** over time? If so, it isn't state. +* Is it **passed in from a parent** via props? If so, it isn't state. +* **Can you compute it** based on existing state or props in your component? If so, it *definitely* isn't state! + +What's left is probably state. + +Let's go through them one by one again: + +1. The original list of products is **passed in as props, so it's not state.** +2. The search text seems to be state since it changes over time and can't be computed from anything. +3. The value of the checkbox seems to be state since it changes over time and can't be computed from anything. +4. The filtered list of products **isn't state because it can be computed** by taking the original list of products and filtering it according to the search text and value of the checkbox. + +This means only the search text and the value of the checkbox are state! Nicely done! + + + +#### Props vs State {/*props-vs-state*/} + +There are two types of "model" data in React: props and state. The two are very different: + +* [**Props** are like arguments you pass](/learn/passing-props-to-a-component) to a function. They let a parent component pass data to a child component and customize its appearance. For example, a `Form` can pass a `color` prop to a `Button`. +* [**State** is like a component’s memory.](/learn/state-a-components-memory) It lets a component keep track of some information and change it in response to interactions. For example, a `Button` might keep track of `isHovered` state. + +Props and state are different, but they work together. A parent component will often keep some information in state (so that it can change it), and *pass it down* to child components as their props. It's okay if the difference still feels fuzzy on the first read. It takes a bit of practice for it to really stick! + + + +## Step 4: Identify where your state should live {/*step-4-identify-where-your-state-should-live*/} + +After identifying your app’s minimal state data, you need to identify which component is responsible for changing this state, or *owns* the state. Remember: React uses one-way data flow, passing data down the component hierarchy from parent to child component. It may not be immediately clear which component should own what state. This can be challenging if you’re new to this concept, but you can figure it out by following these steps! + +For each piece of state in your application: + +1. Identify *every* component that renders something based on that state. +2. Find their closest common parent component--a component above them all in the hierarchy. +3. Decide where the state should live: + 1. Often, you can put the state directly into their common parent. + 2. You can also put the state into some component above their common parent. + 3. If you can't find a component where it makes sense to own the state, create a new component solely for holding the state and add it somewhere in the hierarchy above the common parent component. + +In the previous step, you found two pieces of state in this application: the search input text, and the value of the checkbox. In this example, they always appear together, so it makes sense to put them into the same place. + +Now let's run through our strategy for them: + +1. **Identify components that use state:** + * `ProductTable` needs to filter the product list based on that state (search text and checkbox value). + * `SearchBar` needs to display that state (search text and checkbox value). +2. **Find their common parent:** The first parent component both components share is `FilterableProductTable`. +3. **Decide where the state lives**: We'll keep the filter text and checked state values in `FilterableProductTable`. + +So the state values will live in `FilterableProductTable`. + +Add state to the component with the [`useState()` Hook.](/reference/react/useState) Hooks are special functions that let you "hook into" React. Add two state variables at the top of `FilterableProductTable` and specify their initial state: + +```js +function FilterableProductTable({ products }) { + const [filterText, setFilterText] = useState(''); + const [inStockOnly, setInStockOnly] = useState(false); +``` + +Then, pass `filterText` and `inStockOnly` to `ProductTable` and `SearchBar` as props: + +```js +
    + + +
    +``` + +You can start seeing how your application will behave. Edit the `filterText` initial value from `useState('')` to `useState('fruit')` in the sandbox code below. You'll see both the search input text and the table update: + + + +```jsx src/App.js +import { useState } from 'react'; + +function FilterableProductTable({ products }) { + const [filterText, setFilterText] = useState(''); + const [inStockOnly, setInStockOnly] = useState(false); + + return ( +
    + + +
    + ); +} + +function ProductCategoryRow({ category }) { + return ( + + + {category} + + + ); +} + +function ProductRow({ product }) { + const name = product.stocked ? product.name : + + {product.name} + ; + + return ( + + {name} + {product.price} + + ); +} + +function ProductTable({ products, filterText, inStockOnly }) { + const rows = []; + let lastCategory = null; + + products.forEach((product) => { + if ( + product.name.toLowerCase().indexOf( + filterText.toLowerCase() + ) === -1 + ) { + return; + } + if (inStockOnly && !product.stocked) { + return; + } + if (product.category !== lastCategory) { + rows.push( + + ); + } + rows.push( + + ); + lastCategory = product.category; + }); + + return ( + + + + + + + + {rows} +
    NamePrice
    + ); +} + +function SearchBar({ filterText, inStockOnly }) { + return ( +
    + + +
    + ); +} + +const PRODUCTS = [ + {category: "Fruits", price: "$1", stocked: true, name: "Apple"}, + {category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"}, + {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"}, + {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"}, + {category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"}, + {category: "Vegetables", price: "$1", stocked: true, name: "Peas"} +]; + +export default function App() { + return ; +} +``` + +```css +body { + padding: 5px +} +label { + display: block; + margin-top: 5px; + margin-bottom: 5px; +} +th { + padding-top: 5px; +} +td { + padding: 2px; +} +``` + +
    + +Notice that editing the form doesn't work yet. There is a console error in the sandbox above explaining why: + + + +You provided a \`value\` prop to a form field without an \`onChange\` handler. This will render a read-only field. + + + +In the sandbox above, `ProductTable` and `SearchBar` read the `filterText` and `inStockOnly` props to render the table, the input, and the checkbox. For example, here is how `SearchBar` populates the input value: + +```js {1,6} +function SearchBar({ filterText, inStockOnly }) { + return ( +
    + +``` + +However, you haven't added any code to respond to the user actions like typing yet. This will be your final step. + + +## Step 5: Add inverse data flow {/*step-5-add-inverse-data-flow*/} + +Currently your app renders correctly with props and state flowing down the hierarchy. But to change the state according to user input, you will need to support data flowing the other way: the form components deep in the hierarchy need to update the state in `FilterableProductTable`. + +React makes this data flow explicit, but it requires a little more typing than two-way data binding. If you try to type or check the box in the example above, you'll see that React ignores your input. This is intentional. By writing ``, you've set the `value` prop of the `input` to always be equal to the `filterText` state passed in from `FilterableProductTable`. Since `filterText` state is never set, the input never changes. + +You want to make it so whenever the user changes the form inputs, the state updates to reflect those changes. The state is owned by `FilterableProductTable`, so only it can call `setFilterText` and `setInStockOnly`. To let `SearchBar` update the `FilterableProductTable`'s state, you need to pass these functions down to `SearchBar`: + +```js {2,3,10,11} +function FilterableProductTable({ products }) { + const [filterText, setFilterText] = useState(''); + const [inStockOnly, setInStockOnly] = useState(false); + + return ( +
    + +``` + +Inside the `SearchBar`, you will add the `onChange` event handlers and set the parent state from them: + +```js {4,5,13,19} +function SearchBar({ + filterText, + inStockOnly, + onFilterTextChange, + onInStockOnlyChange +}) { + return ( + + onFilterTextChange(e.target.value)} + /> +