-
Notifications
You must be signed in to change notification settings - Fork 10
Lesson 1.6
Design patterns are solution blueprints to common software development problems. In React, they are methods to solve common problems experienced by React developers.
Component composition is a type of design pattern (or concept) that can be used to break a complex component down to smaller components, and then composing those smaller components to structure and build your application.
This technique prevents us from building too many similar components containing duplicate code and allows us to build fewer components that can be reused anywhere in an application. Thus making them easier to understand and maintain for your team.
For example:
A Button component that accepts title and onClick properties and renders a button tag:
const Button = ({ title, onClick }) => <button onClick={onClick}>{title}</button>;
Any component can render other components - that’s composition.
A Navigation component can then render that Button - that composes other components:
const Navigation = () => {
return (
<>
<Button title="Create" onClick={onClickHandler} />
... // some other navigation code
</>
);
};
With those components and their composition, we can implement as complicated UI as we want
Container Components
Compoments can accept a special prop called children, for which React has it’s own syntax.
const Button = ({ children, onClick }) => <button onClick={onClick}>{children}</button>;
The children syntax is special and looks like normal HTML tags.
Anything can go into children.
const Navigation = () => {
return (
<>
<Button onClick={onClickHandler}>
<Icon />
<span>Create</span>
</Button>
...
// some other navigation code
</>
)
}
Navigation controls what goes into children, from Button’s perspective it just renders whatever the consumer wants.
- Inside
/srcdirectory, create a new file calledInputWithLabel.jsx - Open
/src/InputWithLabel.jsx - Declare and export a new functional React component named
InputWithLabel - Move label and input JSX from
AddTodoForm.jsxtoInputWithLabel.jsx(see below)- Open
/src/AddTodoForm.jsx - Cut (copy and remove) the label and input elements
- Open
/src/InputWithLabel.jsx - Inside the multi-line return, paste the elements you copied (hint: use a Fragment)
- Add
propsas a parameter in theInputWithLabelfunction - Update
todoTitleandhandleTitleChangereferences to come fromprops
- Open
- Refactor
AddTodoForm.jsxto use newInputWithLabelcomponent and pass the necessary props - Run your application and view in browser
- Verify that your "Add Todo Form" still appears correctly
Great, now we have a reusable component! But what if we wanted to reuse this "Input with Label" in a different form? The "Label" is hard-coded as "Title" which isn't very reusable. Let's fix that:
- Open
/src/InputWithLabel.jsx - Replace the text inside the label element with a new
propsvariable namedlabel - Open
/src/AddTodoForm.jsx - Pass a
labelprop to theInputWithLabelcomponent with value"Title" - View your application in browser
- Verify that your "Add Todo Form" still appears correctly

- Open
/src/InputWithLabel.jsx - Replace
labelprop withchildrenso that any child node(s) are used as the label text - Open
/src/AddTodoForm.jsx - Refactor the
InputWithLabelcomponent- Remove the
labelprop - Change the component to have an open/close tag instead of being self-closing
- Pass the text
Titleinside the component tags
- Remove the
- View your application in browser
- Verify that your "Add Todo Form" still appears correctly
- Open
/src/InputWithLabel.jsx - Add
autoFocusprop to input element - View your application in browser
- Verify that input element is focused on page load

Now the input is focused automatically, but what happens when you submit the "Add Todo" form? Focus is lost! Let's update our code so the input element is focused on every render:
- Open
/src/InputWithLabel.jsx - Use the
useRefReact hook to create an imperative ref namedinputRef - Define a
useEffectReact hook without dependency list - Inside the side-effect handler function, call the
focus()method on the currentinputRef - Remove the
autoFocusprop on the input element - Add a
refprop with valueinputRefon the input element - View your application in browser
- Verify that input element is focused on page load
- Enter a new todo in "Add Todo" form and submit
- Verify that input element is re-focused automatically
- Open
/src/TodoListItem.jsx - Add a button element, type "button", inside the list item with text "Remove"
- Open
/src/App.jsx - Define a new handler function named
removeTodowith parameterid- Inside this function, remove the item with the given
idfromtodoList- hint:
filterorsplicemethods
- hint:
- Call the
setTodoListstate setter and pass the new or modified Array
- Inside this function, remove the item with the given
- Pass
removeTodoas a callback handler prop namedonRemoveTodoto theTodoListcomponent - Open
/src/TodoList.jsx - Pass
onRemoveTodoprop as a callback handler prop namedonRemoveTodoto theTodoListItemcomponent - Open
/src/TodoListItem.jsx - Add an
onClickprop to the button element and pass a function that callsonRemoveTodofrom props with the current item id as an argument - View your application in browser
- Click the "Remove" button next to any list item
- Verify that the corresponding item is removed from the list
- Refresh the page and verify that the item is still removed
