Componentizing our React app

At this point, our app is a monolith. Before we can make it do things, we need to break it apart into manageable, descriptive components. React doesn’t have any hard rules for what is and isn’t a component – that’s up to you! In this article we will show you a sensible way to break our app up into components.

Prerequisites: Familiarity with the core HTML , CSS ,和 JavaScript languages, knowledge of the terminal/command line .
Objective: To show a sensible way of breaking our todo list app into components.

Defining our first component

Defining a component can seem tricky until you have some practice, but the gist is:

  • If it represents an obvious "chunk" of your app, it's probably a component
  • If it gets reused often, it's probably a component.

That second bullet is especially valuable: making a component out of common UI elements allows you to change your code in one place and see those changes everywhere that component is used. You don't have to break everything out into components right away, either. Let's take the second bullet point as inspiration and make a component out of the most reused, most important piece of the UI: a todo list item.

Make a <Todo />

Before we can make a component, we should create a new file for it. In fact, we should make a directory just for our components. The following commands make a components directory and then, within that, a file called Todo.js . Make sure you're in the root of your app before you run these!

mkdir src/components
touch src/components/Todo.js

					

Our new Todo.js file is currently empty! Open it up and give it its first line:

import React from "react";

					

Since we're going to make a component called Todo , you can start adding the code for that to Todo.js too, as follows. In this code, we define the function and export it on the same line:

export default function Todo() {
  return (
  );
}

					

This is OK so far, but our component has to return something! Go back to src/App.js , copy the first <li> from inside the unordered list, and paste it into Todo.js so that it reads like this:

export default function Todo() {
  return (
    <li className="todo stack-small">
      <div className="c-cb">
        <input id="todo-0" type="checkbox" defaultChecked={true} />
        <label className="todo-label" htmlFor="todo-0">
          Eat
        </label>
      </div>
      <div className="btn-group">
        <button type="button" className="btn">
          Edit <span className="visually-hidden">Eat</span>
        </button>
        <button type="button" className="btn btn__danger">
          Delete <span className="visually-hidden">Eat</span>
        </button>
      </div>
    </li>
  );
}

					

注意: Components must always return something. If at any point in the future you try to render a component that does not return anything, React will display an error in your browser.

我们的 Todo component is complete, at least for now; now we can use it. In App.js , add the following line near the top of the file to import Todo :

import Todo from "./components/Todo";

					

With this component imported, you can replace all of the <li> elements in App.js with <Todo /> component calls. Your <ul> should read like this:

<ul
  role="list"
  className="todo-list stack-large stack-exception"
  aria-labelledby="list-heading"
>
  <Todo />
  <Todo />
  <Todo />
</ul>

					

When you look back at your browser, you'll notice something unfortunate: your list now repeats the first task three times!

Our todo list app, with todo components repeating because the label is hardcoded into the component

We don't only want to eat; we have other things to — well — to do. Next we'll look at how we can make different component calls render unique content.

Make a unique <Todo />

Components are powerful because they let us re-use pieces of our UI, and refer to one place for the source of that UI. The problem is, we don't typically want to reuse all of each component; we want to reuse most parts, and change small pieces. This is where props come in.

What's in a 名称 ?

In order to track the names of tasks we want to complete, we should ensure that each <Todo /> component renders a unique name.

App.js , give each <Todo /> a name prop. Let’s use the names of our tasks that we had before:

<Todo name="Eat" />
<Todo name="Sleep" />
<Todo name="Repeat" />

				

When your browser refreshes, you will see… the exact same thing as before. We gave our <Todo /> some props, but we aren't using them yet. Let's go back to Todo.js and remedy that.

First modify your Todo() function definition so that it takes props as a parameter. You can console.log() your props as we did before, if you'd like to check that they are being received by the component correctly.

Once you're confident that your component is getting its props , you can replace every occurrence of Eat with your 名称 prop. Remember: when you're in the middle of a JSX expression, you use curly braces to inject the value of a variable.

Putting all that together, your Todo() function should read like this:

export default function Todo(props) {
  return (
    <li className="todo stack-small">
      <div className="c-cb">
        <input id="todo-0" type="checkbox" defaultChecked={true} />
        <label className="todo-label" htmlFor="todo-0">
          {props.name}
        </label>
      </div>
      <div className="btn-group">
        <button type="button" className="btn">
          Edit <span className="visually-hidden">{props.name}</span>
        </button>
        <button type="button" className="btn btn__danger">
          Delete <span className="visually-hidden">{props.name}</span>
        </button>
      </div>
    </li>
  );
}

				

现在 your browser should show three unique tasks. Another problem remains though: they're all still checked by default.

Our todo list, with different todo labels now they are passed into the components as props

Is it completed ?

In our original static list, only Eat was checked. Once again, we want to reuse most of the UI that makes up a <Todo />   component, but change one thing. That's a good job for another prop!  Give each <Todo /> call in App.js a new prop of completed . The first ( Eat ) should have a value of true ; the rest should be false :

<Todo name="Eat" completed={true} />
<Todo name="Sleep" completed={false} />
<Todo name="Repeat" completed={false} />

				

As before, we must go back to Todo.js to actually use these props. Change the defaultChecked 属性在 <input /> so that its value is equal to the completed prop. Once you’re done, the Todo component's <input /> element will read like this:

<input id="todo-0" type="checkbox" defaultChecked={props.completed} />

				

And your browser should update to show only Eat being checked:

Our todo list app, now with differing checked states - some checkboxes are checked, others not

If you change each <Todo /> component’s completed prop, your browser will check or uncheck the equivalent rendered checkboxes accordingly.

Gimme some id , please

Right now, our <Todo /> component gives every task an id attribute of todo-0 . This is bad HTML because id 属性 must be unique (they are used as unique identifiers for document fragments, by CSS, JavaScript, etc.). This means we should give our component an id prop that takes a unique value for each Todo .

To follow the same pattern we had initially, let's give each instance of the <Todo /> component an ID in the format of todo-i ,其中 i gets larger by one every time:

<Todo name="Eat" completed={true} id="todo-0" />
<Todo name="Sleep" completed={false} id="todo-1" />
<Todo name="Repeat" completed={false} id="todo-2" />

				

Now go back to Todo.js and make use of the id prop. It needs to replace the value of the id 属性在 <input /> element, as well as the value of its label's htmlFor 属性:

<div className="c-cb">
  <input id={props.id} type="checkbox" defaultChecked={props.completed} />
  <label className="todo-label" htmlFor={props.id}>
    {props.name}
  </label>
</div>

				

So far, so good?

We’re making good use of React so far, but we could do better! Our code is repetitive. The three lines that render our <Todo /> component are almost identical, with only one difference: the value of each prop.

We can clean up our code with one of JavaScript's core abilities: iteration. To use iteration, we should first re-think our tasks.

Tasks as data

Each of our tasks currently contains three pieces of information: its name, whether it has been checked, and its unique ID. This data translates nicely to an object. Since we have more than one task, an array of objects would work well in representing this data.

src/index.js , make a new const beneath the final import, but above ReactDOM.render() :

const DATA = [
  { id: "todo-0", name: "Eat", completed: true },
  { id: "todo-1", name: "Sleep", completed: false },
  { id: "todo-2", name: "Repeat", completed: false }
];

				

Next, we'll pass DATA to <App /> as a prop, called tasks . The final line of src/index.js should read like this:

ReactDOM.render(<App tasks={DATA} />, document.getElementById("root"));

				

This array is now available to the App component as props.tasks . You can console.log() it to check, if you’d like.

注意: ALL_CAPS constant names have no special meaning in JavaScript; they’re a convention that tells other developers "this data will never change after being defined here”.

Rendering with iteration

To render our array of objects, we have to turn each one into a <Todo /> component. JavaScript gives us an array method for transforming data into something else: Array.prototype.map() .

Above the return statement of App() , make a new const called taskList and use map() to transform it. Let's start by turning our tasks array into something simple: the 名称 of each task:

const taskList = props.tasks?.map(task => task.name);

				

Let’s try replacing all the children of the <ul> with taskList :

<ul
  role="list"
  className="todo-list stack-large stack-exception"
  aria-labelledby="list-heading"
>
  {taskList}
</ul>

				

This gets us some of the way towards showing all the components again, but we’ve got more work to do: the browser currently renders each task's name as unstructured text. We’re missing our HTML structure — the <li> and its checkboxes and buttons!

Our todo list app with the todo item labels just shown bunched up on one line

To fix this, we need to return a <Todo /> component from our map() function — remember that JSX allows us to mix up JavaScript and markup structures! Let's try the following instead of what we have already:

 const taskList = props.tasks.map(task => <Todo />);

				

Look again at your app; now our tasks look more like they used to, but they’re missing the names of the tasks themselves.  Remember that each task we map over has the id , 名称 ,和 checked properties we want to pass into our <Todo /> component. If we put that knowledge together, we get code like this:

const taskList = props.tasks.map(task => (
  <Todo id={task.id} name={task.name} completed={task.completed} />
));

				

Now the app looks like it did before, and our code is less repetitive.

Unique keys

Now that React is rendering our tasks out of an array, it has to keep track of which one is which in order to render them properly. React tries to do its own guesswork to keep track of things, but we can help it out by passing a key prop to our <Todo /> components. key is a special prop that's managed by React – you cannot use the word key for any other purpose.

Because keys should be unique, we're going to re-use the id of each task object as its key. Update your taskList constant like so:

const taskList = props.tasks.map(task => (
    <Todo
      id={task.id}
      name={task.name}
      completed={task.completed}
      key={task.id}
    />
  )
);

				

You should always pass a unique key to anything you render with iteration. Nothing obvious will change in your browser, but if you do not use unique keys, React will log warnings to your console and your app may behave strangely!

Componentizing the rest of the app

Now that we've got our most important component sorted out, we can turn the rest of our app into components. Remembering that components are either obvious pieces of UI, or reused pieces of UI, or both, we can make two more components:

  • <Form/>
  • <FilterButton/>

Since we know we need both, we can batch some of the file creation work together with a terminal command. Run this command in your terminal, taking care that you're in the root directory of your app:

touch src/components/Form.js src/components/FilterButton.js

				

The <Form />

打开 components/Form.js and do the following:

  • Import React at the top of the file, like we did in Todo.js .
  • Make yourself a new Form() component with the same basic structure as Todo() , and export that component.
  • 拷贝 <form> tags and everything between them from inside App.js , and paste them inside Form() ’s return 语句。
  • Export 表单 at the end of the file.

您的 Form.js file should read like this:

import React from "react";
function Form(props) {
  return (
    <form>
      <h2 className="label-wrapper">
        <label htmlFor="new-todo-input" className="label__lg">
          What needs to be done?
        </label>
      </h2>
      <input
        type="text"
        id="new-todo-input"
        className="input input__lg"
        name="text"
        autoComplete="off"
      />
      <button type="submit" className="btn btn__primary btn__lg">
        添加
      </button>
    </form>
  );
}
export default Form;

			

The <FilterButton />

Do the same things you did to create Form.js inside FilterButton.js , but call the component FilterButton() and copy the HTML for the first button inside the <div> element with the class of 过滤 from App.js return 语句。

The file should read like this:

import React from "react";
function FilterButton(props) {
  return (
    <button type="button" className="btn toggle-btn" aria-pressed="true">
      <span className="visually-hidden">Show </span>
      <span>all </span>
      <span className="visually-hidden"> tasks</span>
    </button>
  );
}
export default FilterButton;

		

注意: You might notice that we are making the same mistake here as we first made for the <Todo /> component, in that each button will be the same. That’s fine! We’re going to fix up this component later on, in Back to the filter buttons .

Importing all our components

Let's make use of our new components.

Add some more import statements to the top of App.js , to import them.

Then, update the return statement of App() so that it renders our components. When you’re done, App.js will read like this:

import React from "react";
import Form from "./components/Form";
import FilterButton from "./components/FilterButton";
import Todo from "./components/Todo";
function App(props) {
  const taskList = props.tasks.map(task => (
    <Todo
        id={task.id}
        name={task.name}
        completed={task.completed}
        key={task.id}
      />
    )
  );
  return (
    <div className="todoapp stack-large">
      <h1>TodoMatic</h1>
      <Form />
      <div className="filters btn-group stack-exception">
        <FilterButton />
        <FilterButton />
        <FilterButton />
      </div>
      <h2 id="list-heading">3 tasks remaining</h2>
      <ul
        role="list"
        className="todo-list stack-large stack-exception"
        aria-labelledby="list-heading"
      >
        {taskList}
      </ul>
    </div>
  );
}
export default App;

		

With this in place, we’re almost ready to tackle some interactivity in our React app!

摘要

And that's it for this article — we've gone into some depth on how to break up your app nicely into components, and render them efficiently. Now we'll go on to look at how we handle events in React, and start adding some interactivity.

In this module

发现此页面有问题吗?

最后修改: , 由 MDN 贡献者

  1. Complete beginners start here!
  2. Web 快速入门
    1. Getting started with the Web overview
    2. 安装基本软件
    3. What will your website look like?
    4. 处理文件
    5. HTML 基础
    6. CSS 基础
    7. JavaScript 基础
    8. 发布您的网站
    9. How the Web works
  3. HTML — Structuring the Web
  4. HTML 介绍
    1. Introduction to HTML overview
    2. Getting started with HTML
    3. What's in the head? Metadata in HTML
    4. HTML text fundamentals
    5. Creating hyperlinks
    6. Advanced text formatting
    7. Document and website structure
    8. Debugging HTML
    9. Assessment: Marking up a letter
    10. Assessment: Structuring a page of content
  5. 多媒体和嵌入
    1. Multimedia and embedding overview
    2. Images in HTML
    3. Video and audio content
    4. From object to iframe — other embedding technologies
    5. Adding vector graphics to the Web
    6. Responsive images
    7. Assessment: Mozilla splash page
  6. HTML 表格
    1. HTML tables overview
    2. HTML table basics
    3. HTML Table advanced features and accessibility
    4. Assessment: Structuring planet data
  7. CSS — Styling the Web
  8. CSS 第一步
    1. CSS first steps overview
    2. What is CSS?
    3. Getting started with CSS
    4. How CSS is structured
    5. How CSS works
    6. Using your new knowledge
  9. CSS 构建块
    1. CSS building blocks overview
    2. Cascade and inheritance
    3. CSS 选择器
    4. The box model
    5. Backgrounds and borders
    6. Handling different text directions
    7. Overflowing content
    8. Values and units
    9. Sizing items in CSS
    10. Images, media, and form elements
    11. Styling tables
    12. Debugging CSS
    13. Organizing your CSS
  10. 样式化文本
    1. Styling text overview
    2. Fundamental text and font styling
    3. Styling lists
    4. Styling links
    5. Web fonts
    6. Assessment: Typesetting a community school homepage
  11. CSS 布局
    1. CSS layout overview
    2. Introduction to CSS layout
    3. Normal Flow
    4. Flexbox
    5. Grids
    6. Floats
    7. 位置
    8. Multiple-column Layout
    9. Responsive design
    10. Beginner's guide to media queries
    11. Legacy Layout Methods
    12. Supporting Older Browsers
    13. Fundamental Layout Comprehension
  12. JavaScript — Dynamic client-side scripting
  13. JavaScript 第一步
    1. JavaScript first steps overview
    2. What is JavaScript?
    3. A first splash into JavaScript
    4. What went wrong? Troubleshooting JavaScript
    5. Storing the information you need — Variables
    6. Basic math in JavaScript — Numbers and operators
    7. Handling text — Strings in JavaScript
    8. Useful string methods
    9. 数组
    10. Assessment: Silly story generator
  14. JavaScript 构建块
    1. JavaScript building blocks overview
    2. Making decisions in your code — Conditionals
    3. Looping code
    4. Functions — Reusable blocks of code
    5. Build your own function
    6. Function return values
    7. 事件介绍
    8. Assessment: Image gallery
  15. 引入 JavaScript 对象
    1. Introducing JavaScript objects overview
    2. Object basics
    3. 对象原型
    4. Object-oriented programming concepts
    5. Classes in JavaScript
    6. Working with JSON data
    7. Object building practice
    8. Assessment: Adding features to our bouncing balls demo
  16. 异步 JavaScript
    1. Asynchronous JavaScript overview
    2. General asynchronous programming concepts
    3. Introducing asynchronous JavaScript
    4. Cooperative asynchronous Java​Script: Timeouts and intervals
    5. Graceful asynchronous programming with Promises
    6. Making asynchronous programming easier with async and await
    7. Choosing the right approach
  17. 客户端侧 Web API
    1. 客户端侧 Web API
    2. Introduction to web APIs
    3. Manipulating documents
    4. Fetching data from the server
    5. Third party APIs
    6. Drawing graphics
    7. Video and audio APIs
    8. Client-side storage
  18. Web forms — Working with user data
  19. Core forms learning pathway
    1. Web forms overview
    2. Your first form
    3. How to structure a web form
    4. Basic native form controls
    5. The HTML5 input types
    6. Other form controls
    7. Styling web forms
    8. Advanced form styling
    9. UI pseudo-classes
    10. Client-side form validation
    11. Sending form data
  20. Advanced forms articles
    1. How to build custom form controls
    2. Sending forms through JavaScript
    3. CSS property compatibility table for form controls
  21. Accessibility — Make the web usable by everyone
  22. Accessibility guides
    1. Accessibility overview
    2. What is accessibility?
    3. HTML: A good basis for accessibility
    4. CSS and JavaScript accessibility best practices
    5. WAI-ARIA basics
    6. Accessible multimedia
    7. Mobile accessibility
  23. Accessibility assessment
    1. Assessment: Accessibility troubleshooting
  24. Tools and testing
  25. Client-side web development tools
    1. Client-side web development tools index
    2. Client-side tooling overview
    3. Command line crash course
    4. Package management basics
    5. Introducing a complete toolchain
    6. Deploying our app
  26. Introduction to client-side frameworks
    1. Client-side frameworks overview
    2. Framework main features
  27. React
    1. Getting started with React
    2. Beginning our React todo list
    3. Componentizing our React app
    4. React interactivity: Events and state
    5. React interactivity: Editing, filtering, conditional rendering
    6. Accessibility in React
    7. React resources
  28. Ember
    1. Getting started with Ember
    2. Ember app structure and componentization
    3. Ember interactivity: Events, classes and state
    4. Ember Interactivity: Footer functionality, conditional rendering
    5. Routing in Ember
    6. Ember resources and troubleshooting
  29. Vue
    1. Getting started with Vue
    2. Creating our first Vue component
    3. Rendering a list of Vue components
    4. Adding a new todo form: Vue events, methods, and models
    5. Styling Vue components with CSS
    6. Using Vue computed properties
    7. Vue conditional rendering: editing existing todos
    8. Focus management with Vue refs
    9. Vue resources
  30. Svelte
    1. Getting started with Svelte
    2. Starting our Svelte Todo list app
    3. Dynamic behavior in Svelte: working with variables and props
    4. Componentizing our Svelte app
    5. Advanced Svelte: Reactivity, lifecycle, accessibility
    6. Working with Svelte stores
    7. TypeScript support in Svelte
    8. Deployment and next steps
  31. Angular
    1. Getting started with Angular
    2. Beginning our Angular todo list app
    3. Styling our Angular app
    4. Creating an item component
    5. Filtering our to-do items
    6. Building Angular applications and further resources
  32. Git and GitHub
    1. Git and GitHub overview
    2. Hello World
    3. Git Handbook
    4. Forking Projects
    5. About pull requests
    6. Mastering Issues
  33. Cross browser testing
    1. Cross browser testing overview
    2. Introduction to cross browser testing
    3. Strategies for carrying out testing
    4. Handling common HTML and CSS problems
    5. Handling common JavaScript problems
    6. Handling common accessibility problems
    7. Implementing feature detection
    8. Introduction to automated testing
    9. Setting up your own test automation environment
  34. Server-side website programming
  35. 第一步
    1. First steps overview
    2. Introduction to the server-side
    3. Client-Server overview
    4. Server-side web frameworks
    5. Website security
  36. Django Web 框架 (Python)
    1. Django web framework (Python) overview
    2. 介绍
    3. 设置开发环境
    4. Tutorial: The Local Library website
    5. Tutorial Part 2: Creating a skeleton website
    6. Tutorial Part 3: Using models
    7. Tutorial Part 4: Django admin site
    8. Tutorial Part 5: Creating our home page
    9. Tutorial Part 6: Generic list and detail views
    10. Tutorial Part 7: Sessions framework
    11. Tutorial Part 8: User authentication and permissions
    12. Tutorial Part 9: Working with forms
    13. Tutorial Part 10: Testing a Django web application
    14. Tutorial Part 11: Deploying Django to production
    15. Web application security
    16. Assessment: DIY mini blog
  37. Express Web Framework (node.js/JavaScript)
    1. Express Web Framework (Node.js/JavaScript) overview
    2. Express/Node introduction
    3. Setting up a Node (Express) development environment
    4. Express tutorial: The Local Library website
    5. Express Tutorial Part 2: Creating a skeleton website
    6. Express Tutorial Part 3: Using a database (with Mongoose)
    7. Express Tutorial Part 4: Routes and controllers
    8. Express Tutorial Part 5: Displaying library data
    9. Express Tutorial Part 6: Working with forms
    10. Express Tutorial Part 7: Deploying to production
  38. Further resources
  39. Common questions
    1. HTML questions
    2. CSS questions
    3. JavaScript questions
    4. Web mechanics
    5. Tools and setup
    6. Design and accessibility