Understand JSX in depth and discover advanced patterns to encapsulate common behaviour via higher-order components and render props. Then, learn how to test and debug your application.
Learning Objectives
- Define the types of children within JSX
- Use JSX to apply React component composition with children.
- Describe the process and purpose of creating higher-order components.
- Describe the process and purpose of creating render props.
- Write a test for a React application using Jest and React Testing Library.
- JSX Deep Dive
- Video: JSX, Components and Elements
- Video: The importance of performance to software development
- Video: Component composition with children
- Reading: Types of Children
- Video: Manipulating children dynamically in JSX
- Lab: Exercise: Build a Radio Group Component
- Step 1
- Reading: Solution: Build a Radio Group Component
- Practice Quiz: Self-review: Build a Radio Group Component
- Video: Spread Attributes
- Practice Quiz: Knowledge check: JSX
- Reading: Additional resources
- Reusing behavior
- Video: Cross-cutting concerns in React
- Reading: Higher-order components
- Video: Create a HOC for cursor position
- Video: Render props
- Lab: Exercise: Implementing scroller position with render props
- Reading: Solution: Implementing scroller position with render props
- Practice Quiz: Self-review: Implementing scroller position with render props
- Practice Quiz: Knowledge check: Reusing behavior
- Reading: Additional resources
- Integration tests with React Testing Library
- Video: Why React Testing Library
- Video: Writing the first test for your form
- Lab: Exercise: Writing more test scenarios
- Reading: Solution: Writing more test scenarios
- Practice Quiz: Self-review: Writing more test scenarios
- Reading: Introduction to continuous integration
- Video: Style guides
- Practice Quiz: Knowledge check: Automated testing
- Video: Module summary: JSX and Testing
- Quiz: Module quiz: JSX and Testing
- Reading: Additional resources
JSX Deep Dive
Video: JSX, Components and Elements
JSX is a syntax extension to JavaScript that React uses to describe what the UI should look like. It is a more powerful abstraction that combines both markup and business logic into an entity called a component.
When React analyzes the rendering methods from all the components, it takes the whole JSX tree and creates an intermediary representation. This representation is another tree similar to the original, but where each node instead of being JSX is a plain object describing a component instance or DOM node and its desired properties. This plain object is what React defines as an element.
An element is just a way to represent the final HTML output as a plain object. It consists primarily of two attributes, type and props. Type defines the type of node such as a button and props encompasses all the properties the component receives in a single object.
The type of an element can also be a function corresponding to a React component. When React sees an element with a function type, it will know to ask that component what element it renders to with the given props. React will keep repeating this process until it knows the underlying DOM tag elements for every component on the page.
Once React finishes the process of identifying all user defined components from the tree of elements, it will convert them into DOM elements. The result is what is generally known as the virtual DOM.
When there is a new change in the UI, React will take all the JSX and produce a new UI representation as a tree of elements. It will then compare it with the previous representation that is kept in memory. React will calculate the difference in the tree and apply the minimum number of changes to the underlying DOM nodes in order to process the update.
This is the beauty of the React declarative programming model. React takes care of updating the DOM efficiently, while developers can focus on describing what the UI should look like.
JSX, Components, and Elements in React
JSX
JSX is a syntax extension to JavaScript that allows developers to write HTML-like code within JavaScript files. JSX is not required to use React, but it is a popular choice because it makes it easier to write and maintain React code.
JSX code is converted to plain JavaScript objects by the React compiler. These objects are called React elements.
Components
A React component is a reusable piece of UI that can be rendered to the DOM. Components can be created using either JSX or plain JavaScript.
JSX components are created using the function
keyword. The name of the function is the name of the component. The function takes one argument, which is an object of props. Props are used to pass data to the component.
Plain JavaScript components are created using the React.createClass()
method. The createClass()
method takes an object as its argument. The object can contain properties such as render()
, componentDidMount()
, and componentWillUnmount()
.
Elements
A React element is a plain JavaScript object that represents a DOM node. Elements are created using the React.createElement()
method. The createElement()
method takes three arguments: the type of the element, the props for the element, and the children of the element.
The type of the element can be a string, which represents the name of a DOM node, or a function, which represents a React component.
The props for the element are an object of key-value pairs. The keys are the names of the props, and the values are the values of the props.
The children of the element can be other elements, strings, or numbers.
Example
The following code is an example of a React component written in JSX:
JavaScript
function Button(props) {
return (
<button onClick={props.onClick}>
{props.text}
</button>
);
}
The Button
component takes two props: onClick
and text
. The onClick
prop is a function that is called when the button is clicked. The text
prop is the text that is displayed on the button.
The Button
component returns an element that represents a <button>
DOM node. The onClick
prop is passed to the <button>
element as the onClick
attribute. The text
prop is passed to the <button>
element as the text content.
The following code is an example of how to render the Button
component:
JavaScript
const element = <Button onClick={() => console.log('clicked!')} text="Click me!" />;
ReactDOM.render(element, document.getElementById('root'));
The ReactDOM.render()
method renders the element
to the DOM element with the ID root
.
Conclusion
JSX, components, and elements are the fundamental building blocks of React. By understanding these concepts, you can start building your own React applications.
Which of the examples below are valid types for Elements? Select all that apply.
type: “button”
That’s correct. This is an example of how React specifies a ‘type’ attribute when performing a tree transformation with simple DOM nodes.
type: DeleteButton
Where DeleteButton is a React component.
That’s correct. React would identify a component named DeleteButton as a function and ask that component what element it renders to, with the given props.
type: “div”
That’s correct. This is an example of how React specifies a type attribute when performing a tree transformation with simple DOM nodes.
At this point, you may be wondering
how to define what JSX actually is. And it may not be entirely clear how
react works with JSX to describe a UI and produce a user friendly and effective app. Well, let’s explore these
concepts in a bit more detail. In this video, you’ll learn how
React uses JSX to describe a UI, the differences between components and
elements and the general concepts behind
the React declarative model. The Little Lemon restaurant owners have
been happy with their original website for a long time. But as they have grown as a business,
they now want to add more features, functionality, analytics and
user interactivity. They are noticing that the original
website was built in a way that means there are many limitations when it
comes to these types of enhancements. After seeking some advice, they have concluded that they should
develop an app using JSX and React. They want to understand more about JSX and
React so that they can build a rationale
into their business plan. So let’s get started with JSX. JSX is a syntax extension to
JavaScript that React uses to describe what the UI should look like. However, even though JSX looks like HTML, it’s essentially a more powerful
abstraction that combines both markup and business logic into an entity
called a component. So what is really happening from
the moment you write JSX in your render function until Reacts creates
the desired assets for your pages? To understand all the steps involved, you need to be introduced to
the concept of elements in React. By now, you already have a good
understanding of components and how your entire UI is
represented by a tree of them. You also know that the final
web pages that React produces are nothing more than pure HTML,
CSS, and JavaScript. When react analyzes your rendering
methods from all your components, it takes the whole JSX tree,
starting from the root and creates an intermediary representation. This representation is essentially
another tree similar to before, but where each node instead of being
JSX is a plain object describing a component instance or dom node and
its desired properties. This plane object is what
React defines as an element. An element is just a way to represent
the final HTML output as a plain object. It consists primarily of two attributes,
type and props. Type defines the type of
node such as a button and props encompasses all the properties
the component receives in a single object. Observe how the elements can be nested as
in the button example via the children property. When react creates the entire
element tree starting from the root, the root element specifies all the child
elements as the children prop. And each child element does the same thing
until it reaches the end of the tree. What is important about this new
structure is that both child and parent elements are just descriptions and
not actual instances. In other words, they don’t refer
to anything on the screen when you create them,
they’re just objects after all. But these objects are easy to traverse and of course are simpler than
the actual DOM elements. So far,
you have been introduced to an example of a tree transformation with
simple DOM nodes like a button. In the element tree, this is specified
as the type property, but the type of an element can also be a function
corresponding to a React component. Imagine you have created a component
to encapsulate the traditional HTML button named Submit button. In this case, the type property of the element will
point to the name of the component. This is the fundamental idea of React,
both user defined components and dom nodes can be nested and
mixed with each other in the elementary. For example,
if you were creating a log out process for the Little Lemon restaurant app, you could
do this with a log out component in JSX. In this log out component, The JSX would translate to
the following tree of elements. That allows you to mix and match
components and dom elements as the type property without worrying about whether
Submit button renders to a button, a diff or something else entirely. This keeps components
decoupled from each other, expressing their relationships
through composition. When react sees an element with a function
type like the submit button, it will know to ask that component what element
it renders to with the given props. So React will ask Submit button
again what it renders to and it will transform that into an element. React will keep repeating this process
until it knows the underlying DOM tag elements for
every component on the page. Once React finishes the process of
identifying all user defined components from the tree of elements,
it will convert them into DOM elements. The result is what is generally
known as the virtual DOM. A JavaScript alternative
representation of the real DOM. Now, what are the steps involved when
there is a new change in your UI? First, React will take all your JSX and produce a new UI representation
as a tree of elements. Second, it will compare
it with the previous representation that is kept in memory. Third, it will calculate the difference
in a tree, recall that since each node in the tree is a JavaScript object,
this diffing operation is very fast. And finally based on that difference, it
will apply the minimum number of changes to the underlying dom nodes in order
to process the update, and that’s it. You are probably starting to appreciate
the beauty of the React declared of programming model. In this video, you have learned how
react uses JSX to describe a UI, the differences between components and
elements and the general concept behind
the React declarative model. You have also seen the react transforms
your JSX into an internal tree of elements, which are just
JavaScript objects. And it is that lightweight representation
that lets React update your UI in a predictable way while being fast enough
for high performance applications.
Video: The importance of performance to software development
Performance is a critical aspect of software development. Small changes can make a big difference in the performance of an application. React is a popular JavaScript library that is known for its efficient rendering. Engineers should look for bottlenecks in their code and refactor it to improve performance. They should also use established design patterns to improve performance.
Interesting about performance it’s one of these
things that you can make it or break it with
just one line of change. Of course, sometimes it’s possible that in order to
improve the performance, you have to rewrite an entire
application and start over. But sometimes small changes
make a big difference. My name is Murtadha.
I’m a software engineer at Meta based in Seattle office. At Meta we really tried to think about the different
diversity and the different backgrounds
that users come from and what technologies they might be using to access our products. Thinking about performance
can make a big difference between gaining a user
or losing a user. Because if the application
is not performing, it’s not responsive,
it’s not fast enough, then people will not use it. It’s a big deal for
us to think about performance when we’re
developing these applications. It’s never that our applications are just performance out of
the box and we try to look for the things that can
improve their performance. Performance is usually
something that we build incrementally and we get to it by making
incremental changes. Because we may
think that this is not important and it’s more exciting to build a new feature. It’s more exciting to make
something more colorful. Performance is this
invisible thing that you may not
notice it right away, you may not be able to
show it and brag about it, but it’s actually
really important. It’s an underlying part of the application
that without it, you can’t really have
the applications. One of the things that
React does really well and it’s what has made it excel and become so popular
is that it’s very efficient in building and
rendering a web applications. What React does really
well is that it localizes the change such that if you need to
change a small text, you can only have the work for training that small
text instead of having to rewrite the entire
page or it can be very costly for a
computer to execute. We try to look for
bottlenecks and then go dive deeper into this and see why
is this components so slow? Why is it taking so long? How can we re-factor this
component to make it faster? Maybe we can delegate some of the computation to
the back-end so that we’re not incurring this cost on the user on the front-end. The engineers and the product
managers and designers, they all use the application while it’s being
developed as much as possible to be in the shoes
of a user and really feel the experience that
they’re going through when they’re using
our applications. Then once there’s
enough confidence that this application is ready after checking
all the boxes in terms of its performance, its stability, its usability, and as well as completeness. Then we release it in an
Alpha stage where there’s a small number of users trying it out so we can
get some feedback. As we gain more and more
confidence throughout the feedback cycle
than we crank it up to more users until it’s
completely released. It’s a very important part
of the requirements for an application to be
performance and usable. In order to achieve that, there are certain
design patterns and well established approaches
that engineers have developed over time
that have known to work really well and increase performance
of applications. Like using memoization or delegating part of the
processing to the back-end. I encourage you to look for
those and find ways that you can introduce these design
patterns in your application.
Video: Component composition with children
React component composition is a powerful technique for building reusable and robust components. It is based on two key features: containment and specialization.
Containment refers to the fact that some components don’t know their children ahead of time. For these components, the recommended approach is to use the children prop to pass children elements directly as their content.
Specialization defines components as being special cases of other components. For example, a confirmation dialog is a special case of dialog.
To demonstrate component composition, the author builds a generic dialog component that can be used to create a confirmation dialog for deleting a user account. The generic dialog component uses the children prop to render its content. The confirmation dialog component is a specialized version of the generic dialog component.
The author concludes by stating that component composition is an important technique for building reusable and robust React components.
Component composition with children in React
React component composition is a technique for building reusable and robust components by combining smaller components together. It is based on the idea that you can create a complex component by breaking it down into smaller, more manageable components.
One of the key features of component composition is the children prop. The children prop is a special prop that all components have, and it allows you to pass other components as children of that component.
To use the children prop, you simply pass the children components as arguments to the component that you are rendering. For example, the following code shows how to render a button component with a text label:
JavaScript
const Button = ({ children }) => {
return <button>{children}</button>;
};
const App = () => {
return <Button>Click me!</Button>;
};
In this example, the Button component is passed the string “Click me!” as a child. The Button component then renders that string as the text of the button.
You can also pass multiple components as children to a component. For example, the following code shows how to render a dialog component with a title and a description:
JavaScript
const Dialog = ({ children }) => {
return <div className="dialog">{children}</div>;
};
const App = () => {
return (
<Dialog>
<h1>Delete Account</h1>
<p>Are you sure you want to delete your account? This action cannot be undone.</p>
</Dialog>
);
};
In this example, the Dialog component is passed two components as children: a heading component and a paragraph component. The Dialog component then renders these components as the contents of the dialog.
Component composition is a powerful technique that can help you to write more reusable and maintainable code. By breaking down your components into smaller, more manageable components, you can make it easier to add new features and fix bugs.
Here are some tips for using component composition effectively:
- Use the children prop to pass other components as children of your components.
- Break down your components into smaller, more manageable components.
- Give your components meaningful names.
- Use props to pass data between components.
- Use state to manage the data within a component.
- Use pure functions to write code that is easy to test and reuse.
Here is an example of how to use component composition to build a reusable navigation bar component:
JavaScript
const NavLink = ({ children, to }) => {
return <a href={to}>{children}</a>;
};
const NavigationBar = ({ children }) => {
return <nav>{children}</nav>;
};
const App = () => {
return (
<NavigationBar>
<NavLink to="/">Home</NavLink>
<NavLink to="/about">About</NavLink>
<NavLink to="/contact">Contact</NavLink>
</NavigationBar>
);
};
In this example, the NavLink component is a reusable component that can be used to render a navigation link. The NavLink component takes two props: children and to. The children prop is the text of the navigation link, and the to prop is the URL of the page that the navigation link should link to.
The NavigationBar component is a reusable component that renders a navigation bar. The NavigationBar component takes one prop: children. The children prop is an array of NavLink components.
The App component renders the NavigationBar component and passes an array of NavLink components as children.
This is just one example of how to use component composition to build reusable and maintainable React components. By following the tips above, you can use component composition to write code that is more efficient, easier to test, and more maintainable.
Which of the statements below clearly states the definitions of Containment and Specialization? Select all that apply.
Specialization defines components as being “special cases” of other components.
That’s correct! Specialization defines components as being “special cases” ofother components. For example, the ConfirmationDialog is a special case of Dialog.
Containment refers to the fact that some components don’t know their children ahead of time.
That’s correct! Containment refers to the fact that some components don’t know their children ahead of time. And can also be described as generic boxes, like a Dialog or Alert.
When designing React components, one of the most important
properties developers tend to overlook is
the children prop. The children prop which is a special property
all components have, is the foundation for the React powerful
composition model. Say the Little Lemon
restaurant wants to enable users to have
accounts on their app, this would mean processes would need to be built to create, manage, and delete
accounts in the app. These types of processes can
be constructed simply and efficiently using component
composition with children. In this video, you will learn why component composition is so important and how to effectively use and
appreciate the children prop. You will also learn about two key features of composition, and you’ll explore some
practical examples that will help you
understand how you can leverage it so your components are more robust and reusable. There are two main
features that enable component composition;
containment and specialization. Let’s break down these
two main features now starting with containment. Containment refers
to the fact that some components don’t know
their children ahead of time. This is especially
common for components like a sidebar or a dialog, where they delimit
a specific area in your UI to contain
other elements. You can think of them
also as generic boxes. In case you don’t know, a dialog is a type of modal
window where the rest of the UI is disabled until the modal is addressed
and interacted with. For these component boxes, the recommended approach is
to use the children prop to pass children elements
directly as their content. Let’s explore this
with a dialog example. Here you have a dialog component
which acts as the box, taking care of styling the container to make it
look like a modal window. By using the children prop, it has become a generic
component to which we can provide any valid
JSX as children. To illustrate that, the confirmation dialog
component has been defined, which uses the dialog
component and renders as children a title
and a description. This example also showcases
the second feature of component composition,
specialization. Specialization
defines components as being special cases
of other components. In this example, the
confirmation dialog is a special case of dialog. Now that you are familiar with the basics of
component composition, let’s go ahead and code an application to demonstrate
what you’ve learned. This application has been
created with Create React app. Imagine Little Lemon
would like to offer an easy way for their users to delete their account
if they want to, the goal is to build a generic dialog
component that will contain a title, a description, and a warning button to make
sure users are aware of the action’s consequences all using component composition. I have already created
two generic components, a button and an alert. The button uses the children
prop to specify its text, and the alert is a
generic box that renders an overlay in the background and a white modal in the
center of the screen. The children prop determines
the content of that modal. The first step is to
create a warning button using the specialization feature of component composition. For that, I’ll define a new component
named delete button, where I’ll render the button
component and configure its properties to have a red
color and the text delete. Then I’ll go ahead and
render the alert component. As it stands, it’s just a generic white
box or container. This illustrates the
second feature of component composition
which is containment. I can customize the content
of the box in any way I would like by just providing
JSX as its children. To fulfill the requirements
of Little Lemon, I’ll create a header
titled delete account, as well as a paragraph to inform the user
about the action. I want to state clearly
that they will miss out on the chef’s delicious recipes if they delete their account, so I’ll reflect that
in the description. Finally, the last piece is
to render the delete button as defined previously.
That wraps it up. In this video, you have learned
about a React technique called component composition
and why it is so important. You also learned how to embrace
the two key features of composition; containment
and specialization. You’ve explored some
practical examples that helps you to
create components that are more robust and reusable using the
special children prop.
Reading: Types of Children
Reading
In JSX expressions, the content between an opening and closing tag is passed as a unique prop called children. There are several ways to pass children, such as rendering string literals or using JSX elements and JavaScript expressions. It is also essential to understand the types of JavaScript values that are ignored as children and don’t render anything. Let’s explore these in a bit more detail:
String literals
String literals refer to simple JavaScript strings. They can be put between the opening and closing tags, and the children prop will be that string.
<MyComponent>Little Lemon</MyComponent>
In the above example, the children prop in MyComponent will be simply the string “Little Lemon”.
There are also some rules JSX follows regarding whitespaces and blank lines you need to bear in mind, so that you understand what to expect on your screen when those edge cases occur.
1. JSX removes whitespaces at the beginning and end of a line, as well as blank lines:
<div> Little Lemon </div>
<div>
Little Lemon
</div>
2. New lines adjacent to tags are removed:
<div>
Little Lemon
</div>
3. JSX condenses new lines that happen in the middle of string literals into a single space:
<div>
Little
Lemon
</div>
That means that all the instances above render the same thing.
JSX Elements
You can provide JSX elements as children to display nested components:
<Alert>
<Title />
<Body />
</Alert>
JSX also enables mixing and matching different types of children, like a combination of string literals and JSX elements:
<Alert>
<div>Are you sure?</div>
<Body />
</Alert>
JSX also enables mixing and matching different types of children, like a combination of string literals and JSX elements:
<Alert>
<div>Are you sure?</div>
<Body />
</Alert>
A React component can also return a bunch of elements without wrapping them in an extra tag. For that, you can use React Fragments either using the explicit component imported from React or empty tags, which is a shorter syntax for a fragment. A React Fragment component lets you group a list of children without adding extra nodes to the DOM. You can learn more about fragments in the additional resources unit from this lesson.
The two code examples below are equivalent, and it’s up to your personal preference what to choose, depending on whether you prefer explicitness or a shorter syntax:
return (
<React.Fragment>
<li>Pizza margarita</li>
<li>Pizza diavola</li>
</React.Fragment>
);
return (
<>
<li>Pizza margarita</li>
<li>Pizza diavola</li>
</>
);
JavaScript Expressions
You can pass any JavaScript expression as children by enclosing it within curly braces, {}. The below expressions are identical:
<MyComponent>Little Lemon</MyComponent>
<MyComponent>{‘Little Lemon’}</MyComponent>
This example is just for illustration purposes. When dealing with string literals as children, the first expression is preferred.
Earlier in the course, you learned about lists. JavaScript expressions can be helpful when rendering a list of JSX elements of arbitrary length:
function Dessert(props) {
return <li>{props.title}</li>;
}
function List() {
const desserts = ['tiramisu', 'ice cream', 'cake'];
return (
<ul>
{desserts.map((dessert) => <Item key={dessert} title={dessert} />)}
</ul>
);
}
Also, you can mix JavaScript expressions with other types of children without having to resort to string templates, like in the example below:
function Hello(props) {
return <div>Hello {props.name}!</div>;
}
Functions
Suppose you insert a JavaScript expression inside JSX. In that case, React will evaluate it to a string, a React element, or a combination of the two. However, the children prop works just like any other prop, meaning it can be used to pass any type of data, like functions.
Function as children is a React pattern used to abstract shared functionality that you will see in detail in the next lesson.
Booleans, Null and Undefined, are ignored
false, null, undefined, and true are all valid children. They simply don’t render anything. The below expressions will all render the same thing:
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>
Again, this is all for demonstration purposes so that you know what to expect on your screen when these special values are used in your JSX.
When used in isolation, they don’t offer any value. However, boolean values like true and false can be useful to conditionally render React elements, like rendering a Modal component only if the variable showModal is true
<div>
{showModal && <Modal />}
</div>
However, keep in mind that React still renders some “false” values, like the 0 number. For example, the below code will not behave as you may expect because 0 will be printed when props.desserts is an empty array:
<div>
{props.desserts.length &&
<DessertList desserts={props.desserts} />
}
</div>
To fix this, you need to make sure the expression before && is always boolean:
<div>
{props.desserts.length > 0 &&
<DessertList desserts={props.desserts} />
}
</div>
<div>
{!!props.desserts.length &&
<DessertList desserts={props.desserts} />
}
</div>
Conclusion
You have learned about different types of children in JSX, such as how to render string literals as children, how JSX elements and JavaScript expressions can be used as children, and the boolean, null or undefined values that are ignored as children and don’t render anything.
Video: Manipulating children dynamically in JSX
The video teaches viewers how to use two React APIs, React.cloneElement and React.children.map, to manipulate children dynamically.
React.cloneElement clones and returns a new copy of a provided element. It can be used to modify children properties, add to children properties, and extend the functionality of children components.
React.children.map is a method that invokes a function in every child contained within its children prop, performing a transformation and returning a new element.
In the video, the speaker uses these two APIs to implement a row component that displays each order in a Little Lemon restaurant on its own line, with horizontal spacing between each item.
Conclusion
React.cloneElement and React.children.map are powerful tools for manipulating children dynamically. They can be used to create more flexible and reusable components.
Manipulating children dynamically in JSX
JSX is a JavaScript syntax extension that allows you to write HTML-like code within your JavaScript code. This makes it easier to create and maintain complex user interfaces.
One of the powerful features of JSX is the ability to manipulate children dynamically. This means that you can change the content of a component’s children at runtime.
There are two main ways to manipulate children dynamically in JSX:
- Using the
React.cloneElement()
API - Using the
React.children.map()
API
Using React.cloneElement()
The React.cloneElement()
API allows you to clone a React element and modify its properties. This includes the ability to add, remove, or modify the element’s children.
To use React.cloneElement()
, you first need to pass it the element you want to clone as the first argument. Then, you can pass in an object containing the new props that you want to apply to the cloned element.
For example, the following code clones a button element and adds a new onClick
prop:
JavaScript
const button = <button>Click Me!</button>;
const clonedButton = React.cloneElement(button, {
onClick: () => {
alert('You clicked the button!');
},
});
You can then use the cloned element in your code as if it were the original element.
Using React.children.map()
The React.children.map()
API allows you to iterate over the children of a React element and apply a transformation to each child.
To use React.children.map()
, you first need to pass it the element whose children you want to iterate over as the first argument. Then, you need to pass in a callback function as the second argument. This callback function will be invoked for each child of the element.
The callback function takes two arguments: the current child element and the index of the child element. The callback function should return a new React element.
For example, the following code iterates over the children of a <ul>
element and adds a new li
element for each child:
JavaScript
const list = <ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>;
const updatedList = React.children.map(list, (child, index) => {
return (
<li key={index}>
{child}
</li>
);
});
The key
prop is used to uniquely identify each child element. This is important for React’s reconciliation algorithm to work properly.
You can then use the updated element in your code as if it were the original element.
Conclusion
Manipulating children dynamically in JSX is a powerful way to create more flexible and reusable components. By using the React.cloneElement()
and React.children.map()
APIs, you can change the content of a component’s children at runtime.
Which of the following operations does the React.cloneElementAPI enable a parent to perform? Select all that apply.
Modify children properties
That’s correct! Props in React are immutable objects, so onceReact.cloneElement has created a copy of the element it can then modify the childrens properties in the copy.
Add to children properties
That’s correct! Props in React are immutable objects, so onceReact.cloneElement has created a copy of the element it can then add to the childrens properties in the copy.
Extend the functionality of children components
That’s correct! Props in React are immutable objects, so onceReact.cloneElement has created a copy of the element it can then extend the functionality of the children components.
React children is one of the special props all
your components have implicitly that along with
the react composition model, enables a new paradigm
of component design. So far you have discovered
how to leverage this special prop
in read mode only, meaning that your components consume it as it is provided. But what if I told you that
you can go even further and transform your
children’s elements in any way, shape, or form. To illustrate, see the Little Lemon
restaurant once a way to visualize a summary of live orders as they
come in from customers. What would be really
useful for the chefs working in the
kitchen is to display each customer order in its own separate row with
the name of the dishes, the amount of each
dish, the total price, the time of submission, and the full name
of the customer. Well, by using a set of new react APIs that will enhance your component
design skills, you will be able to
solve this problem for Little Lemon with a smart
and efficient solution. Let’s start by exploring two of these powerful react APIs. React.cloneElement
and react.children. In this video, you’ll learn how to use both of these react APIs and how they can be used to manipulate the Render
Output dynamically. Let’s explore the
first of these APIs, React.cloneElement
in more detail. This is part of the
react top-level API and it’s used to manipulate
and transform elements. Top-level API refers
to the way you would import those functions
from the react package. You can either import react
as a global object and the top of your file and access them as methods
on that object. Or alternatively
as a named import. Recall that elements are just
plain JavaScript objects. The react uses internally to describe what you want
to appear on the screen. React.cloneElement
effectively clones and returns a new copy of
a provided element. The first argument is the react element you
would like to clone, and the second argument is the prompts that
will be added and merged with the original props
passed into the component. Remember that prompts in
react are immutable objects. You must create a
copy of the element first and perform the
transformation in the copy. That’s exactly what
React.cloneElement allows you to achieve. This API is useful to allow a parent to perform the
following operations; modify children
properties, add to children properties and extend the functionality of
children components. For example you could
add another prop dynamically to the
submit button element illustrated before.
Another important top-level API useful for children manipulation
is react.children, which provides utilities
for dealing with the props.children
data structure. The most important method
is the map function. React.children.map
is very similar to the map function from arrays and invokes a function in every child contained
within its children prop, performing a transformation
and returning a new element. Don’t worry if those APIs
still sound a bit confusing. Let’s get into some code to
give you a better picture. The Little Lemon restaurant is busy receiving live orders. To visualize a summary
of those orders, I have been tasked to display
each one of them in a row, having a fixed
horizontal spacing between each piece
of key information. In this application, there
is an order that has been submitted for a client
requesting a pizza margherita. Each order contains the
name of the dish, amount, total price, time of submission, and full name of the client. I have created a row component that will handle the
separation per item. At the moment, it just returns the children
as they are provided. All the items on screen or
cramped with no separation. Let’s go ahead and use both
React.cloneElement and React.children.map as
the implementation to solve the separation problem. The first thing
I’ll do is use the children.map function to
iterate through each child. So far, I am returning
each child as it is, so still no changes
on the screen. To implement even
horizontal spacing. I am going to add some
custom style that will set some left margin in each child
except for the first one. In order to specify
the amount of space, I will create a new
prop called spacing. There will be the number
of pixels and we’ll use string interpolation to set the margin style
properly in pixels. Now that the style is defined, I need to attach it as the
style prop in each element. In the map function callback, I’ll return a new copy of the element using
React.cloneElement. The second argument is what lets you specify the new props. In this case, I’d like to add a new style prop that will
merge the previous style. If the element is
not the first child, then I’ll also merge the child style object that contains the margin
left statement. Great. The last step then is to use the spacing property
in the row component. Let’s set it at 32
pixels. There you go. Each live order now presents
all the information clearly so that chefs don’t make any mistakes and cook
the wrong pizza. In this video, you have
learned about two new APIs, React.cloneElement and
React.children.map. Providing you with a
powerful tool set to manipulate children
dynamically with ease.
Lab: Exercise: Build a Radio Group Component
Instructions
Task
You’ve learned about some advanced React APIs, React.cloneElement and React.Children.map that allow you to modify the children of a component dynamically. In this exercise, you’ll use these APIs to build a RadioGroup component.
On HTML, an input of type radio offers a checked property to determine whether a radio button is selected or not.
When building a radio group in React, in other words, a component that represents a set of choices where only one can be selected at a time, an initial implementation might look like this:
<RadioOption checked={false} onChange={handleOnChange} value="1" />
<RadioOption checked={true} onChange={handleOnChange} value="2" />
<RadioOption checked={false} onChange={handleOnChange} value="3" />
However, this approach is not ideal because it requires the user of the component to keep track of the state of each radio button, as well as setting state change handlers for each option.
Wouldn’t it be great to remove any redundancies and reduce the state to the bare minimum while still keeping the functionality intact? Well, this is a great example where component composition shines and enables developers to use a more intuitive and simpler set of props to define a component’s API.
Instead of having the user of the component to keep track of the state of each radio button, you can have a parent RadioGroup component that is aware of the current selection and provides a handler to manage any selection change, without you having to explicitly pass the checked and onChange props to each RadioOption component.
The RadioGroup component can then leverage the children prop and use both React.cloneElement and React.Children.map to internally pass the checked and onChange props to each RadioOption child.
<RadioGroup selected={selectedValue} onChange={handleOnChange}>
<RadioOption value="1" />
<RadioOption value="2" />
<RadioOption value="3" />
</RadioGroup>
In this exercise, you are going to implement this exact component API.
Note: Before you begin, make sure you understand how to work with the Coursera Code Lab for the Advanced React course.
Steps
Step 1
If you run npm start and view the app in the browser you’ll notice that the starting React app works as is. The app outputs a simple view with a header and a submit button, but no radio options yet.
Open the App.js file. In there you will already see the desired API for the RadioGroup and RadioOption components. At the moment, they don’t render anything on the screen. You don’t have to change anything in this file, but just understand the set of props involved in the component design.
Step 2
Open the Radio/index.js file. Implement the remaining bits for the RadioGroup component. The RadioOptions variable is initially set to null. Instead, use React.Children.map to iterate over the children and clone each child using React.cloneElement. The result should be assigned to the RadioOptions variable.
Each cloned child should receive two additional props, checked and onChange.
Step 3
Open the Radio/index.js file. The RadioOption component is incomplete. In particular, it’s missing some props in the input element that it renders: value, checked and onChange.
The RadioOption component already receives all those props. Your goal is to connect them to the input element.
When adding the onChange prop to the radio input, which represents the event that gets triggered whenever you interact with it, you can access the value property of the event target to get the value of the newly selected radio option, as per the code below.
const handleChange = (e) => {
const newValueSelected = e.target.value
}
Step 4
Verify that the app works as expected. You should be able to select a radio option and see how the submit button gets enabled as soon as a selection is made.
Tips
The RadioGroup component receives the selected prop, a string that represents the value of the currently selected radio option. However, an individual RadioOption component only cares about whether it is selected or not, via the boolean checked prop. You would have to perform some small business logic inside the RadioGroup component to translate the selected prop to the checked prop that each RadioOption child receives.
Conclusion
In this lab, you learned through a practical example how to leverage both React.cloneElement and React.Children.map APIs to build modular components that embrace the children prop. That allows you to offer a simple API to consumers and, as a result, minimize the amount of external local state required to make them work.
Reading: Solution: Build a Radio Group Component
Reading
Here is the completed solution code for the Radio/index.js file:
import * as React from "react";
export const RadioGroup = ({ onChange, selected, children }) => {
const RadioOptions = React.Children.map(children, (child) => {
return React.cloneElement(child, {
onChange,
checked: child.props.value === selected,
});
});
return <div className="RadioGroup">{RadioOptions}</div>;
};
export const RadioOption = ({ value, checked, onChange, children }) => {
return (
<div className="RadioOption">
<input
id={value}
type="radio"
name={value}
value={value}
checked={checked}
onChange={(e) => {
onChange(e.target.value);
}}
/>
<label htmlFor={value}>{children}</label>
</div>
);
};
Step 1
The API for the RadioGroup component is defined as two props: selected, which is a string that matches one of the RadioOption values and onChange, which is the event that gets called whenever a selection changes, providing the new value as an argument.
<RadioGroup onChange={setSelected} selected={selected}>
<RadioOption value="social_media">Social Media</RadioOption>
<RadioOption value="friends">Friends</RadioOption>
<RadioOption value="advertising">Advertising</RadioOption>
<RadioOption value="other">Other</RadioOption>
</RadioGroup>
Step 2
The RadioOptions variable should be assigned to the return value of React.Children.map, which will be a new React element. The first argument passed to the map function should be the children prop, and the second is a function that gets invoked in every child contained within the children property. Recall that a children prop is a special prop all React components have and that it presents a special data structure, similar to arrays, where you can perform iterations. However, they are not exactly instances of JavaScript arrays. That’s why to iterate over all siblings you should use the special React.children.map API provided by React.
Inside the map projection function, you should first clone the element using React.cloneElement, passing as first argument the target child element and as a second argument a configuration with all new props. The resulting element will have the original element’s props with the new props merged in.
onChange can be passed to each child (RadioOption) as it is and checked is the property the RadioOption uses to determine if the underlying radio input is selected. Since RadioGroup receives a selected property, which is a string pointing to the value of the option that has been selected, checked will be only true for one of the options at any point in time. This is guaranteed by performing an equality check, comparing the RadioOption value prop with the selected value.
Finally, the RadioGroup component returns the new RadioOptions elements by wrapping them in curly braces.
import * as React from "react";
export const RadioGroup = ({ onChange, selected, children }) => {
const RadioOptions = React.Children.map(children, (child) => {
return React.cloneElement(child, {
onChange,
checked: child.props.value === selected,
});
});
return <div className="RadioGroup">{RadioOptions}</div>;
};
Step 3
The RadioOption component now receives two new props implicitly, onChange and checked, that RadioGroup is injecting via children manipulation, as seen in the previous section.
The value prop is already provided explicitly inside the App.js component and children represents the label text for the radio input.
You have to connect the props value, checked and onChange correctly. First, both value and checked props should be passed to the radio input as is. Then, you should use the onChange event from the radio input, retrieve the value property from the event target object and pass it to the onChange prop as the argument as seen below. That completes the implementation of the RadioOption component.
export const RadioOption = ({ value, checked, onChange, children }) => {
return (
<div className="RadioOption">
<input
id={value}
type="radio"
name={value}
value={value}
checked={checked}
onChange={(e) => {
onChange(e.target.value);
}}
/>
<label htmlFor={value}>{children}</label>
</div>
);
};
Step 4
Once you run the application in the browser, you should see something similar to the screenshot below.
The important thing is that the button should be enabled as soon as a selection is made. Don’t worry if nothing happens when you click it, it’s intended. In this exercise, the button click event has no action bound to it.
Practice Quiz: Self-review: Build a Radio Group Component
In the RadioGroup component, when cloning each child element (RadioOption), what’s the condition that determines the value of the new checked prop that gets merged into the existing props of each RadioOption component? Recall that the RadioGroup component has three props – onChange, selected and children – and that each RadioOption component receives two props – value and children.
React.cloneElement(child, {
onChange,
checked: child.props.value === selected,
});
That’s correct, the condition checks if the particular value prop from that radio option matches the selected value. If so, the checked prop will be true, which only can happen for one radio button at a time.
Inside the RadioOption component, what should be the value of the onChange prop from the radio input element? Recall that the RadioOption component receives four props – value, checked, onChange and children.
<input type="radio" onChange={e => onChange(e.target.value)} />
That’s correct, that is the proper implementation to trigger a change in the current selection.
What are the arguments that the React.Children.map function receives?
The first argument is the children prop, and the second argument is a transformation function that returns a new React element.
That’s correct, those are the two arguments the function receives.
Video: Spread Attributes
Spread Operator: Simplify React with Power and Caution
This video dives into the spread operator, a powerful tool for creating flexible and efficient React components. Here’s a breakdown:
What it is:
- Three dots (…) operator that simplifies object manipulation.
- Used for copying, merging, and forwarding props in React.
Benefits:
- Simplified code: Reduces boilerplate by spreading existing object properties.
- Flexible components: Makes components adaptable to different data and props.
- Clean API: Provides a clear and consistent interface for consumers.
Key points:
- Object Operations:
- Copy an object with
{...obj}
. - Merge objects with
{ ...obj1, ...obj2 }
.
- Copy an object with
- React Props:
- Spread props from an object to a component with
{...props}
. - Be mindful of prop order: later props override earlier ones.
- Spread props from an object to a component with
Example:
- Button component: Forwards native button props and adds custom
type
prop. - Login button: Extends
Button
with pre-configuredtype
andonClick
while still spreading props.
Caution:
- Overriding props with spread may not be intended.
- Consider prop order to avoid unexpected behavior.
Takeaways:
- Spread operator is powerful for creating flexible and efficient React components.
- Be aware of potential pitfalls and use it strategically for clean and predictable code.
This summary condenses the key points of the video, focusing on the benefits and applications of the spread operator in React development, while also highlighting potential pitfalls and best practices for its use.
# Spread Operator in React: Mastering Flexibility and Efficiency
Welcome to this tutorial on the spread operator, a versatile tool that can streamline your React development!
Here’s what we’ll cover:
1. What is the Spread Operator?
- Syntax and core functionality
- Object copying and merging
2. Spread Operator in React Props
- Forwarding props to components
- Creating adaptable components
- Maintaining clean APIs
3. Best Practices and Cautions
- Prop order and overriding
- Strategic usage for clarity and predictability
4. Hands-On Examples
- Building a flexible Button component
- Creating a LoginButton with pre-configured props
- Troubleshooting common pitfalls
5. Takeaways and Next Steps
- Key points for effective spread operator usage
- Further exploration and practice
Ready to dive in? Let’s get started!
- In a restaurant ordering system, you can use the spread operator to copy and merge objects. For example, if a customer wants to make a last-minute change to their order, you can use the spread operator to create a new object with the updated information.
- In a shopping cart application, you can use the spread operator to copy and merge objects. For example, when a user adds an item to their cart, you can use the spread operator to create a new object that includes the existing items in the cart as well as the newly added item.
- In a social media application, you can use the spread operator to pass props to different components. For example, when rendering a post, you can use the spread operator to pass all the necessary props to the post component, such as the post content, author information, and comments.
The spread operator is one of the coolest
additions to the JavaScript language. Thanks to the spread operator,
which is represented by three dots. Common operations that previously required
a bit more code like cloning an object or array are now so much simpler. So in this video you will learn how to
use the spread operator and objects, how to spread native
properties to DOM components. How to design flexible components and about the caveats when
using the spread operator. Imagine that the little lemon restaurant
wants to have a simple welcome screen where users can sign up or log in depending on whether
they have an account or not. The sign up and log in buttons are in
nature very similar with differences in the text and
the action taken when they’re clicked. To achieve this implementation,
you could define a button component that encapsulates the native
HTML button element and uses the same props as
its native counterpart. However, a button presents dozens of
different properties for customization. Instead of having to specify each
of them manually, the spread operator can be used to forward all of
them without typing each one manually. React embraces this operator at
its core when it comes to props. But before digging deeper, let’s
refresh with this operator enables for objects in pure JavaScript. The spread operator can be applied to
different data types in JavaScript such as arrays, objects and even strings. Since react props are just objects, this lesson is going to be focused
strictly on the object type. Copying and merging objects are the two
main operations you may perform with these operators. For a simple copy of an object, this
is the syntax required curly braces and the three dots preceding
the object to copy. In this example, order amend represents
a last minute change to the type of pizza ordered by the customer. For merging first you need to spread the
properties from the original object and then provide the new properties to be
added or to replace original properties. The property called item has been
replaced with the new order information. Great, now that
the preliminaries are covered, let’s explore how react
uses the spread operator. This order list component example
renders an order component. Each order component expects four props
an ID, user name, item and price. This first example shows how
you might usually do it, passing all the props in
the return statement explicitly. However, this can be simplified if
you already have the props your order component needs in an object. Here, you can see that only the spread
operator is required in the return statement, saving us time by
just spreading all the props and avoiding having to type them manually. This pattern allows you to
create flexible components but there are also some caveats you need
to keep in mind when using the syntax. Let’s explore this in more detail
with an example demonstration. In this application,
I have created a simple welcome screen for little lemon restaurant where
users are able to sign up or log in depending on whether
they have an account or not. At the top, I have to find a button component that
wraps the native button from the DOM. This component expects the same
props as its native counterpart with the addition of the type prop, which is a
custom prop that determines the background of the button based on the theme provided. Here is a clear example of using
the spread operator to group all the props that belong to the native button and
explicitly extract the custom props I have defined for this component and
our react specific, type and Children. This implementation is clean for developers since they can provide all
the props a native button would expect. The second example is a login button
component that renders the custom button component I have created itself. This login button does
some pre-configuration by fixating some of the props from the button
component in this case type and on click while still passing the native button
props down with the spread operator. Now the app component renders the two
buttons and uses the button component for the sign up and
the login button component for login. The buttons are both configured here
to send the user to the sign up page, unless they have an account in which case
the login button components original on click function will send
them to the login page. I have also provided both with an on
click handler to show an alert about the intended action
when the button is pressed. However, observe how I have mistakenly
provided the same alert message that is used for
sign up on the login button component. Hence, overriding the on click handler
that the login button already defines. Now, can you guess what the message of
the alert will be when I click on it, I’ll give you a few
seconds to think about it. Well, if you guessed logging in,
you guessed right. The reason is that, even though I have overridden the on click
prop in the log in button component, its implementation is done in a way that
prevents that overriding from happening. Why is that because of the order
of the spread operator? If I go ahead and spread the props at
the end instead right after on click. The behavior is different and
signing up is output instead. So what are the main
takeaways from this lesson? The spread operator is a great tool that
enables the creation of more flexible components, especially when forwarding
props automatically to other components that expect them as well as providing
your consumers with a nice component. API. However, keep in mind that depending on the order of the spread, the behavior
may differ so you need to be conscious about your decision when it
comes to component design. In the example of the login button before
it may make sense to prevent the override of the on click property but it may not
be the intended case for other props. Well done. I hope you’re getting an idea of the
multiple benefits that can be gained from using these react tools.
Practice Quiz: Knowledge check: JSX
Let’s suppose you have the below JSX that gets returned from a component, what would be the equivalent object representation (Element) that React will create internally?
{
type: "button",
props: {
className: "button-primary",
children: {
type: "div",
props: {
children: "Submit",
}
},
},
}
That’s correct, the children key also should point to an element, so it has to have two keys: type and props.
What is the concept of component specialization?
A component defined as a special case of another more generic component.
That’s correct. For example, a SubmitButton component is a more specialized version of a Button component.
You would like to clone a React element by using the React.cloneElement API, where the particular element has the below structure:
{
type: SubmitButton,
props: {
color: "blue",
children: "Submit!",
disabled: true,
},
};
That’s correct, the color props gets overridden and a new prop called disabled with a value of true gets added.
Imagine you are using the spread operator in the below component as follows:
{ title: "tiramisu", cal: 500 }
That’s correct, that’s the final value.
Amongst the below expressions, select all that will not render anything on the screen when being used in JSX.
<div>{(() => true)()}</div>
That’s correct, the function gets invoked instantly in place and returns a value of true, which will not render anything.
<div>{false}</div>
That’s correct, the false value will not render anything.
<div>{undefined}</div>
That’s correct, the undefined value will not render anything
<div>{null}</div>
That’s correct, the null value will not render anything.
Reading: Additional resources
Reading
Here is a list of additional resources for JSX Deep Dive:
- Chakra-UI is an open-source component library that embraces all the concepts explained during this lesson, being a nice option if you would like to start your project with a set of atomic components that have been carefully designed with flexibility in mind, so that they can be customized as per your theme requirements.
- Compound components with hooks is an article that illustrates how a combination of component composition, context and hooks can lead to a clean and concise component design.
- React Fragments from the official React docs illustrates how to group a list of React children without adding extra nodes to the DOM.
Reusing behavior
Video: Cross-cutting concerns in React
Summary of the video on cross-cutting concerns in React:
- What are cross-cutting concerns? Generic functionalities needed in many places, like managing permissions, handling errors, or logging, not directly related to app’s core logic.
- Components, not always ideal for reusability: Components are the main unit of reuse, but repeating cross-cutting logic leads to redundancy and complexity.
- Subscription example: Live order and newsletter list components share similar data subscription and update patterns.
- Higher-Order Components (HOCs): A function that takes a component and returns a new one with enhanced capabilities. Perfect for encapsulating cross-cutting logic without modifying original components.
- HOC example:
withSubscription
HOC takes any component and adds live data subscription with configurable data source (orders or users). - Benefits of HOCs: Reuse code, keep components stateless, centralize logic, and improve maintainability.
- Bonus: Briefly mentioned another pattern called Render Props for cross-cutting concerns.
Key takeaways:
- Cross-cutting concerns need efficient reuse strategies beyond components.
- HOCs offer a powerful way to encapsulate and share common logic for different components.
- Using patterns like HOCs leads to cleaner, more maintainable, and reusable React applications.
Tutorial: Handling Cross-Cutting Concerns in React
Welcome to this tutorial on managing cross-cutting concerns in React applications! Cross-cutting concerns are functionalities found across multiple components, like authentication, logging, or error handling. Repeating this logic leads to code duplication and reduces maintainability. This tutorial will explore best practices and patterns for tackling these concerns effectively.
1. Identifying Cross-Cutting Concerns:
- Look for repetitive functionality: Analyze your codebase for logic used in multiple components that isn’t directly related to their specific features.
- Think beyond components: Consider concerns like authorization, data fetching, or state management that span various components.
- Common examples: Authentication, authorization, logging, error handling, data fetching, state management, theming, accessibility.
2. Strategies for Managing Concerns:
- Component Composition: Leverage props and children to pass required functionality down the component tree. Though simple, can become cumbersome for complex concerns.
- Render Props: Pass a function as a prop that renders child components, centralizing the cross-cutting logic. Offers more flexibility and separation of concerns.
- Higher-Order Components (HOCs): Wrap existing components with a function that injects additional functionality and props. Encapsulates logic efficiently and avoids modifying original components.
3. Implementing a Cross-Cutting Concern with HOCs:
Let’s build an HOC for data fetching and state management:
a) Define the HOC:
JavaScript
const withData = (WrappedComponent, fetchData) => {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetchData().then(setData).finally(() => setIsLoading(false));
}, []);
return (props) => (
<WrappedComponent
{...props}
data={data}
isLoading={isLoading}
/>
);
};
- WrappedComponent: The component to be enhanced with data fetching.
- fetchData: Function that retrieves data for the wrapped component.
- data & isLoading: State variables holding fetched data and loading status.
- useEffect: Fetches data on mount and sets state.
- Returns: A function that renders the wrapped component with additional props: data and isLoading.
b) Example Usage:
JavaScript
const ProductList = (props) => {
const { data, isLoading } = props;
if (isLoading) {
return <p>Loading...</p>;
}
return (
<ul>
{data.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
};
const EnhancedProductList = withData(ProductList, async () => {
// Fetch product data from API
});
<EnhancedProductList />;
- ProductList: Component displaying the product list.
- EnhancedProductList: ProductList wrapped with the
withData
HOC. - Data and isLoading props injected from the HOC.
4. Benefits of Using HOCs:
- Code reusability: Encapsulate logic once and use it across multiple components.
- Component separation: Keeps components lean and focused on their specific UI.
- Improved maintainability: Makes cross-cutting logic easier to understand and modify.
- Testability: Simplifies testing of common functionalities.
5. Further Exploration:
- Context API: Provides a mechanism for global state management, useful for certain cross-cutting concerns.
- Redux: Popular state management library for complex applications with extensive cross-cutting logic.
- Libraries & Frameworks: Many libraries and frameworks offer solutions for specific cross-cutting concerns, like authentication or data fetching.
Remember, choosing the right approach depends on the specific concerns and complexity of your application. Experiment with different strategies and find the one that best suits your needs!
Bonus: Keep in mind that using too many HOCs can add complexity and lead to nesting hell. Aim for balance and clarity when structuring your code.
A higher order component (HOC), is an advanced pattern that emerges from React’s compositional nature.
What are some of the benefits of using a HOC in your solution?
- It enhances or extends the capabilities of the component provided.
- You can define logic in a single place, share it across many components, and keep them unchanged and stateless.
That’s correct. A HOC transforms a component into another component. In other words, it enhances or extends the capabilities of the component provided.
When building React
applications, you will find yourself creating some generic
functionality that is not related to the applications
business logic and that is needed
in many places. For example, think about managing different
permission roles, handling errors,
or even logging. They are all required
for any application, but are not necessary from
the business point of view. This group of functionality
is what falls into the category of
cross-cutting concerns. In this video you are
going to learn about cross-cutting concerns and
why components despite being the primary
unit of code reuse in React aren’t always a good
choice for this kind of logic. You will also discover why new patterns were introduced
to solve this problem. Excited to learn more, well, let’s get started. Imagine that you are tasked with building the
logic to display a list of live orders for the little lemon restaurant app. Let’s explore how this
could be achieved. Here is a valid
implementation for a live orders list where local
state is used for storing the current list
of orders and use effect handles both the
subscription and on subscription to the
live data updating the order’s value as soon
as a new order arrives. Now, imagine the little lemon also needs a place in
the application to keep track of the
number of users subscribing to its
newsletter in real-time. This could be a
valid implementation for such functionality. Can you identify
any similarities between live orders list
and newsletter list? They are definitely not
identical since they call different methods on data source and render a different output, but much of the
implementation is the same. Upon examination, they both add a change listener to
data source on mount, set new state whenever
the data source changes and remove the
change listener on unmount. You can imagine that in a large app the same
pattern of subscribing to data source and setting
local state with new data will occur
over and over again. So far you have seen that our custom hook to
encapsulate that logic is one of the solutions at your disposal, however, that introduces the issue of having to alter the
implementation of each component that
needs that data and thus making all
of them stateful. How could you define
the subscription logic in a single place? Share it across
many components and keep them unchanged
and stateless. Well, this is where higher-order components
are a perfect solution. A higher-order component
also known as a HOC is an advanced pattern that emerges from React’s
compositional nature. Specifically, a
higher-order component is a function that takes a component and returns
a new component. Whereas a component
transforms props into UI, a higher-order
components transforms a component into
another component. In other words, it enhances or extends the capabilities
of the component provided. Let’s go ahead and examine
how the implementation of this reusable
subscription logic would look with a
higher-order component. With subscription is the
higher-order component that takes the raft component
you would like to enhance with subscription
capabilities as well as a selectData function
to determine the type of data you are getting
from the data source. In this case, either
orders or users. With subscription then returns a new component that renders the provided component
and supplies a new prop to it called
data that will contain the most recent list of items from the data
source of interest. It also passes other prompts through to
the rapt component, which is a convention in HOCs. That’s the implementation, but how about its usage? In this case you would define two different components configured with
custom parameters, one for live orders
and another for newsletter subscribers
all without having to repeat the subscription
implementation in either live orders and user list making this a much
more efficient solution. There’s still one
more pattern for cross-cutting concerns
that you will learn about soon called
render prompts. Great. In this video, you have learned about
cross-cutting concerns and why components are not always
enough for reusing behavior. You also explored one alternative
pattern to encapsulate common behavior in
React applications which are higher-order
components.
Reading: Higher-order components
Reading
In a previous lesson, you learned about Higher-order components (HOC) as a pattern to abstract shared behavior, as well as a basic example of an implementation.
Let’s dive deeper to illustrate some of the best practices and caveats regarding HOCs.
These include never mutating a component inside a HOC, passing unrelated props to your wrapped component, and maximizing composability by leveraging the Component => Component signature.
Don’t mutate the original component
One of the possible temptations is to modify the component that is provided as an argument, or in other words, mutate it. That’s because JavaScript allows you to perform such operations, and in some cases, it seems the most straightforward and quickest path. Remember that React promotes immutability in all scenarios. So instead, use composition and turn the HOC into a pure function that does not alter the argument it receives, always returning a new component.
const HOC = (WrappedComponent) => {
// Don't do this and mutate the original component
WrappedComponent = () => {
};
…
}
Pass unrelated props through to the Wrapped Component
HOC adds features to a component. In other words, it enhances it. That’s why they shouldn’t drastically alter their original contract. Instead, the component returned from a HOC is expected to have a similar interface to the wrapped component.
HOCs should spread and pass through all the props that are unrelated to their specific concern, helping ensure that HOCs are as flexible and reusable as possible, as demonstrated in the example below:
const withMousePosition = (WrappedComponent) => {
const injectedProp = {mousePosition: {x: 10, y: 10}};
return (originalProps) => {
return <WrappedComponent injectedProp={injectedProp} {...originalProps} />;
};
};
Maximize composability
So far, you have learned that the primary signature of a HOC is a function that accepts a React component and returns a new component.
Sometimes, HOCs can accept additional arguments that act as extra configuration determining the type of enhancement the component receives.
const EnhancedComponent = HOC(WrappedComponent, config)
The most common signature for HOCs uses a functional programming pattern called “currying” to maximize function composition. This signature is used extensively in React libraries, such as React Redux, which is a popular library for managing state in React applications.
const EnhancedComponent = connect(selector, actions)(WrappedComponent);
This syntax may seem strange initially, but if you break down what’s happening separately, it would be easier to understand.
const HOC = connect(selector, actions);
const EnhancedComponent = HOC(WrappedComponent);
connect is a function that returns a higher-order component, presenting a valuable property for composing several HOCs together.
Single-argument HOCs like the ones you have explored so far, or the one returned by the connect function has the signature Component => Component. It turns out that functions whose output type is the same as its input type are really easy to compose together.
const enhance = compose(
// These are both single-argument HOCs
withMousePosition,
withURLLocation,
connect(selector)
);
// Enhance is a HOC
const EnhancedComponent = enhance(WrappedComponent);
Many third-party libraries already provide an implementation of the compose utility function, like lodash, Redux, and Ramda. Its signature is as follows:
compose(f, g, h) is the same as (…args) => f(g(h(…args)))
Caveats
Higher-order components come with a few caveats that aren’t immediately obvious.
- Don’t use HOCs inside other components: always create your enhanced components outside any component scope. Otherwise, if you do so inside the body of other components and a re-render occurs, the enhanced component will be different. That forces React to remount it instead of just updating it. As a result, the component and its children would lose their previous state.
const Component = (props) => {
// This is wrong. Never do this
const EnhancedComponent = HOC(WrappedComponent);
return <EnhancedComponent />;
};
// This is the correct way
const EnhancedComponent = HOC(WrappedComponent);
const Component = (props) => {
return <EnhancedComponent />;
};
- Refs aren’t passed through: since React refs are not props, they are handled specially by React. If you add a ref to an element whose component is the result of a HOC, the ref refers to an instance of the outermost container component, not the wrapped component. To solve this, you can use the React.forwardRef API. You can learn more about this API and its use cases in the additional resources section from this lesson.
Conclusion
And in summary, you have examined higher-order components in more detail. The main takeaways are never mutating a component inside a HOC and passing unrelated props to your wrapped component.
You also learned how to maximize composability by leveraging the Component => Component signature and addressed some caveats about HOC.
Video: Create a HOC for cursor position
Summary:
- Little Lemon restaurant wants to track visitor behavior on their pizza section to understand which pizzas are popular.
- They plan to track mouse position to see which pictures capture attention.
- The video demonstrates using a Higher-Order Component (HOC) to encapsulate the mouse tracking logic.
- The HOC provides mouse position data to other components through props.
- Two example components, panel and point trackers, display the data differently.
- This tracking helped Little Lemon identify a blurry pizza photo affecting sales, which they promptly replaced.
Key takeaways:
- HOCs can improve code reusability by encapsulating cross-cutting concerns like mouse tracking.
- React components can receive and utilize data through props.
- Tracking user behavior can provide valuable insights for website optimization.
When defining a HOC, such as withMousePosition, why is the with part of the HOC name a general convention recommended by React?
It expresses the enhancing nature of the technique.
That’s correct! The with part of the HOC name is a general convention recommended by React, as it expresses the enhancing nature of the technique, like providing a component ‘with’ something else.
The Little Lemon
restaurant is receiving a spike and visits
to their website during the last month due
to customers loving some of the special pizzas the chef
presented to the public. Despite the success,
there are some pizzas that aren’t receiving the
same level of attention, so they have decided
to implement some basic analytics to better understand which are
the bestsellers and which ones are not being
ordered so regularly. For that, they would like
to track the mouse position of the visitors when they
are in the pizza section, so that they know exactly
which pictures are capturing people’s attention and which ones are overlooked. In this video, I’m going to
show you how to implement such functionality by using a higher-order
components or HOC, which will be in charge of
encapsulating the logic and state to track the cursor
position of the visitors. Then I’ll showcase two
different presentation or components that will consume that data and present
it in different ways. Are you ready? Let’s begin. The little application has been created with Create React app, so far in the rendering
part of the app component, there is a header
displaying the title of the restaurant and
two components, panel most logger and
point mouse logger. They both expect a mouse
position prop and will return null if that
prop is not provided. That’s why at the moment they don’t render anything at all. You could implement the
mouse tracking logic in each component but
recall that it would incur code repetition
and duplication of the same logic in
two different places. That’s why the
recommended approach is to use one of
the techniques that React provides for encapsulating
cross-cutting concerns. In this example, I will illustrate how to do
this with an HOC. I call this HOC with
mouse position. The width part of the name is a general convention
recommended by React since it expresses the enhancing nature
of the technique, like providing a component
with something else. Recall that an HOC is
essentially a function that takes a component and
returns a new component. Let’s complete the
initial scaffold and return a component
that will render the wrapped component
provided to the function without forgetting to spread
the props it receives, so that they will pass-through. Great. Now to track the
position of the cursor, I would need to define a
new piece of local state, which I’ll call
mouse position for the data and set mouse
position for the state setter. The initial state will be an
object with two properties, x and y to define the two-dimensional
coordinates on the screen and I will
set both to zero. X equals 0 and y equals 0, represents the top-left
corner of the screen. Next, I would need to
set a global listener in the window object for
the mouse move events. Since this is a side-effect, I need to perform
the subscription and unsubscription logic
inside use effects. Let’s go ahead and do that, I will add a new
event listener for a mouse movement to
the window object. For the call back, I will name the function
handleMousePositionChange. That for now,
doesn’t do anything. It’s important to remove any subscription when
your component unmounts. The way to do that is by
returning a function from use effect and then performing
any cleanup needed. In this case, I would
have to use the window.removeEventListener
passing as arguments, the mouse move event and the same callback
function as before. To complete the logic and set the state with current
mouse position, I need to read that information from the browser event object, which is passed as an
argument to the callback. That event object contains two properties that
define the coordinates, clients X and client Y. I will assign both of them to the
corresponding dimension. Finally, the last
step to complete the implementation is to
set a new prop called mouse position in the
rapt component to pass that information down to all components that are
interested in that data. Great. Now, that
the implementation of the HOC is finished, let’s add the last few pieces to display the mouse
position on the screen. To enhance the two components
previously defined panel mouse logger and
point mouse logger, I will use the HOC to create two new component versions
that will be aware of the mouse position data
and I’ll call them panel mouse tracker and point
mouse tracker respectively. Finally, I will use the enhanced versions in the rendering part of
the app component. Amazing. The requirements
are now finalized. If I move the cursor
around the screen, you can see two
different trackers that display the same information
in different ways. One as a panel and the one
below as a data point. Well, that’s the
end of this video. Little Lemon restaurant now uses this solution to
track their pizza loving customers and have
discovered that something was affecting the sales of
their pizza de Avila. Guess what? After an employee investigated that particular
pizza on the app, they noticed that one of the
photos was a bit blurry, thanks to this little
tracking application, they have now taken action and uploaded new
high-quality photos. The next time a customer
is exploring the menu, they won’t miss the
pizza de Avila.
Video: Render props
The video presents the render props pattern as an alternative to higher-order components for code reusability in React. It uses the analogy of choosing the right utensil for different foods to explain how different techniques are best suited for certain situations.
Here are the key takeaways:
- Render props pattern: A component takes a function called “render” and calls it in its own render logic. This function receives arguments (often data) and returns a React element to be displayed.
- Benefits:
- Encapsulates common logic like data fetching.
- Keeps components independent and easy to maintain.
- Provides flexibility in how data is presented.
- Application example:
- Little Lemon Restaurant wants to display the number of desserts and drinks available.
- A
dataFetcher
component fetches data based on URL and sends it via therender
function. - Separate components display the dessert and drink counts using the received data.
- Comparison to higher-order components:
- Both enhance components without modifying their original implementation.
- Render props injects props dynamically through the
render
function, while HOCs inject props directly.
Overall, the render props pattern is a powerful tool for reusing code and promoting flexibility in React development.
What is the result of the return statement of the DataFetcher component?
The component returns the result of calling the render function.
That’s correct! The DataFetcher component returns the result of calling the render function and has no other rendering business.
So far you have learned about a technique
called higher order components that is used to encapsulate common functionality,
but it’s not the only one available to you. For example, you can choose between
different eating utensils depending on the type of food you’re eating. Some chopsticks for your favorite sushi
dish or a fork for a delicious carbonara. But there’s nothing wrong with using
a fork for eating sushi after all. Not everyone has learned
how to use chopsticks and it’s the tool that makes
the job easier that matters. Well, this is also the case when
it comes to code reusability. There are several tools you can use to
share code between react components, and in this video you are going to
be introduced to a new one, the render props pattern. Say the Little Lemon Restaurant
is looking for a way to keep count of all
the items it has on the menu. They would like to display this
information in the application so they know when they need to replenish
stock and to make it easier for customers to see what is or
isn’t available at any given time. That information is stored on a server. So we would have to be fetched
first prior to displaying it. There are various ways to define
the fetching logic like a higher order component. However, render props is also a powerful
technique you can leverage to reuse common code. The render props technique is so appropriately named that it’s
almost self explanatory. It’s about using a prop called render
with a particular attribute that it has to be a function. To be more precise, a component with a
render prop takes a function that returns a react element and calls that
function inside its render logic. For example a meal provider component
could use this pattern if you remember a higher order component
enhanced a component by providing new props into it with render props. However the new props are injected
dynamically as the parameter of the function. You may already have recognized
some similarities between the two. The end goal is the same to enhance
components without modifying their original implementation. The way in which they
inject those new props or enhancements is what differs to
better illustrate this point. Let’s explore an application that
showcases the implementation of a component that uses
a render prop in this example the little lemon restaurant would like
to keep account of all the desserts and drinks that it has in the menu. That information will need to be fetched
from a server that they control and displayed in the application
with a single piece of text. The logic for fetching data is one
example of a cross cutting concern. Multiple components may depend on
data that lives somewhere else but you don’t want to implement
the same fetching logic over and over in every component that needs it. Right this is a good place to use
the render props pattern to abstract this functionality to
showcase how it works. I have created a component called
data fetcher whose only purpose is to fetch data based on a U. R.
L. This is one of its props but it’s the second one that should catch your
attention and that’s the render prop. I’m not fetching real data in this
example but I have created a mock if else statement that will return
either a list of all desserts or drinks available based on the U. R. L. Path as usual. This fetching logic is a side effect
that should be placed inside use effect. Finally let’s move to
the return statement. This is the unusual part. The component is returning the result
of calling the render function and has no other rendering business. This is what makes it very flexible. Data fetcher only has one
purpose fetching data and the way they receive it is via the
argument in the render function which is the local state used to store
the list of desserts or drinks. It’s then up to the developers
to decide how they would like to present that data on the screen. Finally, let’s explore the two
presentational components. I have defined to show the number of
desserts and drinks available in the menu. The desserts count component uses a
particular endpoint to fetch desserts and uses a paragraph element
as the render prop, displaying the number of
desserts as a single text. The same applies to the drinks count
component with the difference being that he utilizes another U. R.
L. And instead shows a header element
reflecting the number of drinks. Finally, the app component
renders both of them, and the results
are displayed on the screen. You have learned about another technique
you can use in addition to higher order components, which is
the render props pattern. Hopefully, now that little lemon
keeps a live count of drinks, they can replenish their fridge
with your favorite refreshments.
Lab: Exercise: Implementing scroller position with render props
Reading
Task
You’ve learned about render props and how it’s a viable alternative to higher-order components (HOCs) to encapsulate cross-cutting concerns. In a previous video, you saw a possible implementation of a mouse position tracker using higher-order components. In this exercise, you’ll implement the same specifications but using a render prop component instead.
Note: Before you begin, make sure you understand how to work with the Coursera Code Lab for the Advanced React course.
Steps
Step 1
Open the App.js file.
Complete the implementation of the MousePosition component. Specifically, you’ll need to:
- Implement the body ofhandleMousePositionChangeinsideuseEffect
- Implement the return statement of the component.
Step 2
Tweak the implementation of PanelMouseLogger. The requirements are:
- The component should not receive any props.
- The component should not have any if statements.
- The component should leverage theMousePosition render prop to show the coordinates in a panel fashion. The panel UI is already provided to you, your goal is to connect the UI with the mouse position data.
Step 3
Tweak the implementation of PointMouseLogger. The requirements are:
- The component should not receive any props.
- The component should not have any if statements.
- The component should leverage theMousePosition render prop to show the coordinates in a point representation. The point UI is already provided to you, your goal is to connect the UI with the mouse position data
Save all the changes and run the app. Preview the updates in the browser, and confirm that the page shows two distinct interfaces that display the safe information (mouse position) in different fashion, one as a panel and another as a point coordinates.
App.js
import "./App.css";
import { useEffect, useState } from "react";
const MousePosition = ({ render }) => {
const [mousePosition, setMousePosition] = useState({
x: 0,
y: 0,
});
useEffect(() => {
const handleMousePositionChange = (e) => {
// Use e.clientX and e.clientY to access the mouse position on the screen
};
window.addEventListener("mousemove", handleMousePositionChange);
return () => {
window.removeEventListener("mousemove", handleMousePositionChange);
};
}, []);
// What should be returned here?
return null;
};
// This component should not receive any props
const PanelMouseLogger = ({mousePosition}) => {
// The below if statement can be removed after the render props pattern is implemented
if (!mousePosition) {
return null;
}
return (
<div className="BasicTracker">
<p>Mouse position:</p>
<div className="Row">
<span>x: {mousePosition.x}</span>
<span>y: {mousePosition.y}</span>
</div>
</div>
);
};
// This component should not receive any props
const PointMouseLogger = ({mousePosition}) => {
// The below if statement can be removed after the render props pattern is implemented
if (!mousePosition) {
return null;
}
return (
<p>
({mousePosition.x}, {mousePosition.y})
</p>
)
};
function App() {
return (
<div className="App">
<header className="Header">Little Lemon Restaurant 🍕</header>
<PanelMouseLogger />
<PointMouseLogger />
</div>
);
}
export default App;
Reading: Solution: Implementing scroller position with render props
Reading
Here is the completed solution code for the App.js file:
import "./App.css";
import { useEffect, useState } from "react";
const MousePosition = ({ render }) => {
const [mousePosition, setMousePosition] = useState({
x: 0,
y: 0,
});
useEffect(() => {
const handleMousePositionChange = (e) => {
setMousePosition({
x: e.clientX,
y: e.clientY,
});
};
window.addEventListener("mousemove", handleMousePositionChange);
return () => {
window.removeEventListener("mousemove", handleMousePositionChange);
};
}, []);
return render({ mousePosition });
};
const PanelMouseLogger = () => {
return (
<div className="BasicTracker">
<p>Mouse position:</p>
<MousePosition
render={({ mousePosition }) => (
<div className="Row">
<span>x: {mousePosition.x}</span>
<span>y: {mousePosition.y}</span>
</div>
)}
/>
</div>
);
};
const PointMouseLogger = () => {
return (
<MousePosition
render={({ mousePosition }) => (
<p>
({mousePosition.x}, {mousePosition.y})
</p>
)}
/>
);
};
function App() {
return (
<div className="App">
<header className="Header">Little Lemon Restaurant 🍕</header>
<PanelMouseLogger />
<PointMouseLogger />
</div>
);
}
export default App;
1. Implement the body of handleMousePositionChange
The mousemove handler function receives an event as parameter that contains the mouse coordinates as clientX and clientY properties. Therefore you can provide a position update by calling the state setter setMousePosition with the new values.
const handleMousePositionChange = (e) => {
setMousePosition({
x: e.clientX,
y: e.clientY,
});
};
2. Implement the return statement of the component
The MousePosition component receives a render prop, which is the special prop name designed by convention to specify a function that returns some JSX. Since the MousePosition component does not take care of any visualization logic, but rather encapsulates cross-cutting concerns, it should return the result of calling the render function with the mousePosition as an argument. In other words, it’s up to the components that consume MousePosition to specify what sort of UI they want to display when they receive a new value of the mouse position on the screen.
return render({ mousePosition })
Step 2
The PanelMouseLogger component should not receive any props. Hence, the early return from the previous implementation if no props were provided is no longer needed.
Instead, the mousePosition is now injected as the first argument of the render function prop that MousePosition uses. It’s in this render function body where the previous JSX should be extracted and returned.
const PanelMouseLogger = () => {
return (
<div className="BasicTracker">
<p>Mouse position:</p>
<MousePosition
render={({ mousePosition }) => (
<div className="Row">
<span>x: {mousePosition.x}</span>
<span>y: {mousePosition.y}</span>
</div>
)}
/>
</div>
);
};
Step 3
Similarly, as in step 2, the component should not receive any props and the early if statement should be removed. The particular UI for this component is provided as part of the render prop as well.
const PointMouseLogger = () => {
return (
<MousePosition
render={({ mousePosition }) => (
<p>
({mousePosition.x}, {mousePosition.y})
</p>
)}
/>
);
};
At this point, the implementation has been completed and you should see the following result when you run the app in the browser:
Practice Quiz: Self-review: Implementing scroller position with render props
Considering the MousePosition component receives a prop called render, which is a function, what are valid options of JSX returned from the component?
return render({ mousePosition });
That’s correct, this component should not return any tags/elements and just call the render function with the mousePosition as argument.
The PointMouseLogger component returns the below JSX.
<p>
({mousePosition.x}, {mousePosition.y})
</p>
After incorporating the MousePosition component as part of the JSX returned by PointMouseLogger, what should be the new JSX that PointMouseLogger returns?
return(
<MousePosition
render={({ mousePosition }) => (
<p>
({mousePosition.x}, {mousePosition.y})
</p>
)}
/>
);
That’s correct, the render prop should now return the previous JSX from the component, with the mousePosition being injected as an argument.
The App component initially presents the below output structure
function App() {
return(
<div className="App">
<header className="Header">Little Lemon Restaurant 🍕</header>
<PanelMouseLogger />
<PointMouseLogger />
</div>
);
}
After adding the MousePosition component into the mix, would you still have to perform any changes to the App component?
No
That’s correct, the App component shouldn’t be altered at all. Only PanelMouseLogger and PointMouseLogger components require changes to read the mousePosition data using the render prop from MousePosition component.
Practice Quiz: Knowledge check: Reusing behavior
When dealing with cross-cutting data in your React applications, what are some of the problems of using a custom hook to encapsulate that logic? Select all that apply.
The fact that you would have to alter the implementation of each component that needs that specific data.
That’s correct, you would have to add the custom hook to each particular component that needs that data.
That it turns a stateless component into a stateful one.
That’s correct, the addition of a custom hook that deals with specific data introduces state into the component.
Here, you can find the APIs of some higher-order components that have been already implemented. Amongst all the options, which ones present an invalid signature that doesn’t follow the convention? Select all that apply.
withSubscription(() => getData(), Component)
That’s correct, the component should come as the first argument and any additional parameters should go afterwards.
What are some of the best practices to follow when implementing the higher-order component pattern? Select all that apply.
Maximize composability.
That’s correct, using proper API definitions allow you to combine different HOCs in a composable manner.
Passed unrelated props through to the Wrapped Component.
That’s correct, you should pass all the other props through to the wrapped component.
What are some of the differences between higher-order components and render props? Select all that apply.
Render props provide the new data as a function parameter, whereas components wrapped with an HOC get the new data as a new prop.
That’s correct, render props do so dynamically inside the JSX and HOCs provide an extra prop.
They inject the new props in the component to be enhanced in a different way.
That’s correct, render props dynamically inside the JSX and HOCs as an extra prop.
True or false. A component with a render prop as renderer can do anything a higher-order component can do.
True
That’s correct, they are just two different implementations for encapsulating cross-cutting concerns, but they serve the same purpose.
Reading: Additional resources
Reading
Here is a list of additional resources as you continue to explore Reusing Behavior:
- Downshift is a popular open-source library that implements an autocomplete, combo box or select experience using the render prop pattern.
- Render props from the official React docs.
- Higher Order Components from the official React docs.
- Forwarding Refs from the official React docs showcases in detail how to forward refs in higher-order components, so that they are passed through properly.
Integration tests with React Testing Library
Video: Why React Testing Library
This video explains the importance of automated testing for React components and how to achieve it using Jest and React Testing Library.
Key points:
- Why testing is important:
- Ensures app functionality before delivery.
- Reduces user complaints and saves time/money.
- Testing best practices:
- Avoid implementation details of components.
- Tests should mimic user interactions.
- Maintainable in the long term.
- Tools:
- Jest: JavaScript test runner with mocking capabilities.
- React Testing Library: Set of utilities for testing React components without relying on implementation details.
- Example test:
- Verifies presence of a link to Little Lemon’s webpage.
- Demonstrates using Jest and React Testing Library functions.
Additional notes:
- The video mentions additional resources for learning more about mocking.
- Future videos will cover writing more complex tests.
Which of the following statements are true about Jest and React Testing Library tool? Select all that apply.
React Testing Library is a set of utilities that let you test React components without relying on their implementation details.
That’s correct! React Testing Library is designed to fulfill all testing best practices out of the box, so that you are able to focus on the business logic your tests need to run assertions on.
Jest is a JavaScript test runner that lets you access an artificial DOM,which provides an approximation of how the browser works.
That’s correct! Jest is a JavaScript test runner that lets you access an artificial DOM called jsdom. While jsdom is only an approximation of how the browser works, it is often good enough for testing React components.
As the developer for Little
Lemon Restaurant application, how could you guarantee that the app you created
works as intended? Well, you may choose to rely on your
methodical ability to manually interact with
all the different parts of the application yourself, ensuring the app is
fully functional. However, manually testing every new incremental
change you’d like to make is
likely to be tedious, error-prone, and time-consuming, especially as your app
grows in complexity. That’s where automation
tests come in. In this video, you
will learn how to write tests properly for
your React components. You will achieve this by exploring why testing
is important, and best practices
when creating tests. Being introduced to
the tools Jest and React Testing Library for
structuring your tests. As well as working through
a practical example of testing a component
using these tools. Just like factories
perform testing of the products they build to ensure that they
work as expected, developers need to do the
same with their code. A well-designed suite of automation tests is
particularly effective in allowing you to
discover defects or bugs before delivery
to the client. In doing so, testing
is important for guaranteeing the quality of
the software you develop. Further, by catching bugs before they find their way into
a live application, testing reduces the number
of user complaints, and can ultimately save an
organization time and money. Now that you have an idea of
why testing is important, what are the best
practices you need to keep in mind when
writing your tests? The first is that your
tests need to avoid including implementation
details of your components. React is just a tool, and your final users will have no notion that React
exists at all. Rather than dealing
with instances of rendered React components, your tests should work
with actual DOM nodes. Secondly, the more you test resemble the way your
software is used, the more confidence
they can give you. Finally, you also want your tests to be maintainable
in the long term. As long as you’re not
changing functionality, any changes in the implementation
of the component won’t break your tests and slow
you and your team down. Now, let’s explore
the two tools that React endorses to
structure your tests, Jest and the React
Testing Library. Jest is a JavaScript
test runner that lets you access an artificial
DOM called JSDOM. While JSDOM is only
an approximation of how the browser works, it is often good enough for
testing React components. Jest provides a good
iteration speed combined with powerful
features like mocking modules, so you can have more control
over how the code executes. Recall that mocking refers to something made as an imitation, and enables you to replace
complex functions from your code with others that are simpler and simulate
the same behavior. Mocking features
can be used to make sure that your unit
testing is standalone. If you’d like to revisit the concept of mocking
in greater depth, you can check out the
additional resources at the end of this lesson. The second tool is
React Testing Library, which is a set of utilities
that let you test React components without relying on their implementation details. React Testing Library
has been designed to fulfill all the best
practices highlighted before, so that you get a
well-configured testing environment
out of the box, and are able to focus on the business logic your tests
need to run assertions on. With the theory covered, let’s go ahead and
implement a test from scratch using Jest and
React Testing Library. When you start a new project
with Create React app, you already get
both Jest and React testing library
pre-installed by default. Plus both tools are
already pre-configured, and there’s an
example test file in your root folder
called app.test.js. Imagine that Little Lemon
has made an agreement with a popular restaurant
aggregator to have its webpage included as a
new URL in its listing. In the app.js file, the app component
renders a link in the page that points to
Little Lemon’s webpage. Let’s go through the
test I created to automatically verify that
the link is always present. The first thing I need
to do is to import both render and screen from the
React Testing Library. The render function is used
to render the component you would like to test and
perform assertions against. Because querying the entire document.body is very common, React Testing Library also
exports a screen object, which is a reference
to that object, and has every query
prebound to it, meaning that it will
automatically ask the whole document
when running a search. Now, to wrap the test scenario, just provides the
global test function, which takes a text description
as the first argument, and a function to
compose all the steps your test needs to go through
as a second argument. This function does not
need to be imported since Jest injects it automatically
in all your test files. The first step is to render the app component in the
artificial DOM environment. Secondly, I used the
screen object to create a query against
the document.body. In this case, I am using the getByText utility
to ask the body tag of the document if it can
find an element inside with a string called
Little Lemon Restaurant, and store the result of that finding in the link
element object. If the search is successful, getByText will return
the found element. Otherwise, it will return null. Finally, to complete the test, I perform an assertion
asking whether the link element from the query above is present
in the document, meaning whether it’s currently
visible on the screen. For that, the global
expect function is used. This is another utility
that Jest incorporates globally without the need
for an explicit import. The expect function
receives the result of a query and appends
the specific matcher. In this example, the
matcher refers to an element visible in
the whole document. If I run the test, it fails. Let’s check the output logs to try to understand
what has gone wrong. It states that it
was unable to find an element with a text,
Little Lemon Restaurant. Interesting. Let’s go ahead and check the app.js
component again. I had made a mistake and typed
orange instead of lemon, something that the test
was able to catch. That’s exactly where you want to see when a failure occurs. Also, you might have noticed how straightforward it is to
write your test assertions. Everything you see in code
translates nicely to how a real user would interact with your app and responds with the behavior that
you would expect. Let’s run the test again now
that the issue is fixed. Great. The test passes and Little Lemon’s online exposure is about to grow even more. In addition to the importance of testing and testing
best practices, you have now learned how to
test your React components using Jest and the
React Test Library. Stay tuned, because soon
you will discover how easy it is to write
more complex tests.
Video: Writing the first test for your form
- Issue: The restaurant received negative reviews without specific feedback, making it challenging to address the problems.
- Solution: To improve, they made the text box mandatory when users rate below five (on a scale of ten). This ensures additional comments are provided.
- Automated Tests: They added automated tests to catch errors caused by faulty updates.
- Feedback Form: The application includes a feedback form with a numerical score input (0-10) and a text box for comments.
- Submit Button Logic:
- If the score is below five, the submit button is disabled.
- Users must add a comment of at least ten characters.
- Code Details:
- The entry point is the
app.js
component. - The feedback form component receives a prop function for submission.
- It manages local state for the input fields.
- The button disabled logic depends on the score and comment length.
- The
handleSubmit
function triggers the parent’s submission function.
- The entry point is the
In summary, The Little Lemon Restaurant implemented changes to gather more meaningful feedback and ensure quality through automated testing. 🍋👍
When writing a part of your test to locate the range input and fill it with a value, which functions do you need to use?
fireEvent.change()
That’s correct. To fill the input and update the state with a value, you can use the fireEvent.change() utility from React Testing Library.
screen.getByLabelText
That’s correct. Screen.getByLabelText asks the root document to find a label tag whose text contains a specified text value and then return the input element associated with that label.
Imagine that The Little Lemon Restaurant
started receiving a few bad reviews from its customers. The problem is that they could not figure
out what was wrong and act accordingly. Since users only provided a low
numerical score, skipping the part of providing additional feedback and
passing that information to the chefs. In order to solve that, they’ve decided
to make the common text box mandatory as long as the score provided by their users
is lower than five in a scale to ten. Additionally, they would like to shield
this new logic with automated tests so whenever any changes are performed,
the test suite would run and be able to catch any potential
errors incurred by a faulty update. So the application consists of a feedback
form that contains a range input for a numerical score ranging
from zero to ten and a text box to incorporate
additional comments. To satisfy the requirements
desired by Little Lemon, the submit button will be disabled
if the score is lower than five, forcing users to add a comment
of at least ten characters. Now let’s examine the code. The entry point is the app dot JS
component where I’m rendering a feedback form component. This component receives a prop on
submit which is a function that contains the values of
the form as parameters so that the parent app component
can perform the submission. The feedback form represents an HTML
form and includes two controlled components via local state,
arranged input, and the text area. There are two pieces to
highlight in this component. The first is the button disabled logic is
disabled is the variable that controls that state and is set to true if
the score is lower than five and the comment has less than ten characters. The other important piece is the handle
submit function which is hooked into the form on submit attribute. When the submit button is clicked,
the handle submit function will be called. This function itself calls the prop
function provided by the parent with the corresponding form values. Well, it’s clear that the feedback form
component contains all the business logic of interest. So let’s go ahead and write a test for
the submission logic. The convention for test is to create them inside a file
that has the dot test extension. That way, jest the test runner is able to
pick them up automatically when you run the test command in your terminal. As I have already written a test scenario,
I will now walk you through each line of code so that you understand how
the test is structured step by step. The test scenario is checking that users
are prevented from submitting the form right away if the score
is lower than five and there is either no additional feedback or
it’s too short. First, I create a new
mock function with jest, recall that a mock function is a special
function that allows you to track how a particular function
is called by external code. When the feedback form calls the function
you provide as the on-submit prop, you will be able to explore
the argument passed in the call. Then, I render the feedback form and pass
the mock function as the on-submit prop. The following steps are needed to locate
the range input and fill it with a value, notice that to find the input I’m
using the screen.get by label text and passing a regular expression
to match against. Screen is a utility object from
react testing library that represents the whole page which
basically translates to asking the root document to find a label tag whose
text contains the word score and then return the input element
associated with that label. To fill the input with the value you have
to use the fire event utility from React testing library and
call the change function. While React-controlled components update
their state via the unchanged prop, React testing library follows
a slightly different convention, removing the on part and
having the update method as lower case. To simulate the form, submission I
have to locate the button element, observe how I’m using
a different query method. Get by roll which looks at elements
with a specific role attribute. This will work well since the HTML button
already has the button role internally set, to simulate the event I
need to provide two arguments. First, the input element in question and second the browser event object which
holds the new value as target.value. To perform a button click I
have to use fireevent.click. It follows the same convention as before
removing the on-site of the prop and having everything in lower case. Great. The final two statements
are the assertions of the test. The first one illustrates an example
of an expect matter that wants to check the opposite by pre-pending the not
before calling the final matter. What it’s asserting is that the function
that handles the submission of the form has not been called, which is what I’m expecting when
an additional comment has been omitted. Furthermore, I have added the second
assertion to make sure the submit button is indeed disabled by using
the to have attributes matter. And that’s it. Well, done for
reaching the end of this demonstration. In this video, you learned how you can
shield your business logic with a few lines of powerful code,
thanks to the React testing library. And what’s more important
it has enabled the chef at Little Lemon to finally receive
the necessary feedback and make sure all the new pizzas have
a bit more extra cheese on top.
Lab: Exercise: Writing more test scenarios
Reading
You’ve learned about Jest and react-testing-library to create automated tests for your components. Now it’s time to write some of your own tests! Remember the Feedback form Little Lemon put together to gather feedback about their recipes? In a previous lesson, you were introduced to a test scenario where the app verified that users who provided less than a score of 5 could only submit their form if feedback was also provided.
In this exercise, you’ll create two more test scenarios to verify the form submission works as expected:
- User is able to submit the form if the score is lower than 5 and additional feedback is provided.
- User is able to submit the form if the score is higher than 5, without additional feedback.
Note: Before you begin, make sure you understand how to work with the Coursera Code Lab for the Advanced React course.
Steps
Step 1
Open the App.test.js file. There you’ll encounter two new test scenarios that should be completed.
Step 2
After writing the test scenarios, run the tests to verify they pass. For that, execute npm test in the terminal.
Tip
Explore the FeedbackForm component to understand the JSX it returns and how you can access the elements in the tests. For more information about react-testing-library queries, check out the documentation.
Conclusion
In this exercise, you gained more experience when writing tests for your React Components. You were introduced to different screen query methods to locate DOM elements and learned about new Jest matchers to perform valuable assertions.
Code
import { fireEvent, render, screen } from "@testing-library/react";
import FeedbackForm from "./FeedbackForm";
describe("Feedback Form", () => {
test("User is able to submit the form if the score is lower than 5 and additional feedback is provided", () => {
const score = "3";
const comment = "The pizza crust was too thick";
const handleSubmit = jest.fn();
render(<FeedbackForm onSubmit={handleSubmit} />);
// You have to write the rest of the test below to make the assertion pass
expect(handleSubmit).toHaveBeenCalledWith({
score,
comment,
});
});
test("User is able to submit the form if the score is higher than 5, without additional feedback", () => {
const score = "9";
const handleSubmit = jest.fn();
render(<FeedbackForm onSubmit={handleSubmit} />);
// You have to write the rest of the test below to make the assertion pass
expect(handleSubmit).toHaveBeenCalledWith({
score,
comment: "",
});
});
});
Reading: Solution: Writing more test scenarios
Reading
Here is the completed solution code for the App.test.js file:
import { fireEvent, render, screen } from "@testing-library/react";
import FeedbackForm from "./FeedbackForm";
describe("Feedback Form", () => {
test("User is able to submit the form if the score is lower than 5 and additional feedback is provided", () => {
const score = "3";
const comment = "The pizza crust was too thick";
const handleSubmit = jest.fn();
render(<FeedbackForm onSubmit={handleSubmit} />);
const rangeInput = screen.getByLabelText(/Score:/);
fireEvent.change(rangeInput, { target: { value: score } });
const textArea = screen.getByLabelText(/Comments:/);
fireEvent.change(textArea, { target: { value: comment } });
const submitButton = screen.getByRole("button");
fireEvent.click(submitButton);
expect(handleSubmit).toHaveBeenCalledWith({
score,
comment,
});
});
test("User is able to submit the form if the score is higher than 5, without additional feedback", () => {
const score = "9";
const handleSubmit = jest.fn();
render(<FeedbackForm onSubmit={handleSubmit} />);
const rangeInput = screen.getByLabelText(/Score:/);
fireEvent.change(rangeInput, { target: { value: score } });
const submitButton = screen.getByRole("button");
fireEvent.click(submitButton);
expect(handleSubmit).toHaveBeenCalledWith({
score,
comment: ""
});
});
});
Steps
Step 1
The first test scenario has the following specification:
User is able to submit the form if the score is lower than 5 and additional feedback is provided
The test scenario already contains some initial code that acts as boilerplate before getting to the bulk of the test, in particular:
- Two variables that hold the desired state of the form, a score of 3 and an additional comment.
- A mock function that is called when submitting the form.
- The rendering of the form component.
- The final assertion that should make the test pass.
If you run as it is, the test will fail stating that the mock function has not been called at all. That is because no interactions have occurred yet and it’s your task to write those.
The first user interaction that needs to happen is to set the score as 3. The following code achieves that:
const rangeInput = screen.getByLabelText(/Score:/);
fireEvent.change(rangeInput, { target: { value: score } });
The first line grabs a reference to the range input component by using the global screen object from react-testing-library and the query getByLabelText to find a label that contains the exact text Score:
Then, a change event is simulated on the input, passing as the event an object with the value property set to the variable score: event.target.value = score
After that, a second user interaction is required to set the additional comment. This is the code that accomplishes that:
const textArea = screen.getByLabelText(/Comments:/);
fireEvent.change(textArea, { target: { value: comment } });
No further explanation is needed regarding those two lines, since they mimic the same interaction as with the range input.
Last but not least, a submission of the form should be simulated by calling the below two lines:
const submitButton = screen.getByRole("button");
fireEvent.click(submitButton);
In this particular instance, the button is referenced by using a different query on the global screen object, getByRole. This query looks for an element whose role attribute is set to “button”, which is inherent in all button HTML tags.
The form is finally submitted via firing a click event on the button instance.
If you run the command npm test in your terminal, the test should pass now.
Let’s now cover the second scenario.
User is able to submit the form if the score is higher than 5, without additional feedback
The below represents the code you need to write to make the test pass.
const rangeInput = screen.getByLabelText(/Score:/);
fireEvent.change(rangeInput, { target: { value: score } });
const submitButton = screen.getByRole("button");
fireEvent.click(submitButton);
This test is simpler and there is nothing new besides skipping any interaction with the text area, since no additional feedback is required when the score provided is higher than 5, being 9 in this scenario.
Step 2
If you run npm test after adding the lines above, both of your tests should pass successfully, with the terminal providing the below output.
Practice Quiz: Self-review: Writing more test scenarios
What’s the correct call to fire an onChange event on an input with react-testing-library’s fireEvent API?
fireEvent.change(input, { target: { value: 'myValue' } });
How would you fire a click event on a submit button with react-testing-library fireEvent API?
fireEvent.click(button);
When locating an element by using one of the screen querying methods from react-testing-library, such as screen.getByRole or screen.getByLabelText, what would be the return value of the call if the element is not found?
An error will be thrown
Correct, these functions will throw an error if the element is not found.
Reading: Introduction to continuous integration
Reading
Introduction
Continuous Integration (CI) is a software development technique in which developers use a version control system, like Git, and push code changes daily, multiple times a day. Instead of building out features in isolation and integrating them at the end of the development cycle, a more iterative approach is employed.
Each merge triggers an automated set of scripts to automatically build and test your application. These scripts help decrease the chances that you introduce errors in your application.
If some of the scripts fail, the CI system doesn’t progress to further stages, issuing a report that developers can use to promptly assess what was wrong and resolve the problem.
This reading will teach you why embracing a CI tool is essential for your software development process. You will also explore a typical development workflow that you can integrate into your CI system and some of the main benefits of using CI.
Why do we need CI?
In new product development, the time to figure everything out up front is limited. Taking smaller steps helps estimate more accurately and validate more often. Having a shorter feedback loop involves more iterations. This number of iterations, not the number of hours invested, drives the process forward.
Working in long feedback loops is risky for software development teams, increasing the chances of introducing errors. Integrating new changes is also time-consuming.
By automating all integration steps and having small controlled changes, developers avoid repetitive work and minimize human errors. The CI tool monitors the central code repository and prevents people from deciding when and how to run tests. Every time there is a new commit, it runs all automated tests.
Based on the outcome, it either accepts the commit if all tests passed successfully or reject it if there was a failure.
CI Pipeline
Below is a graphical representation of a typical CI process as a pipeline. When new code enters one end, a new version of the app gets built automatically, and a suite of automated tests is run against it.
Continuous Integration is a small part of a more significant process called Continuous Delivery. However, that’s outside the scope of the purpose of this lesson, and you can check more information in the additional resources section.
A typical development workflow
Here is a simplified CI workflow that companies often embrace daily as part of their software development process:
- A developer from the team creates a new branch of code in Github, performs changes in the code, and commits them.
- When the developer pushes its work to GitHub, the CI system builds the code on its servers and runs the automated test suite.
- Suppose the CI system detects any error in the CI pipeline. In that case, the developer who pushed the code gets a notification, for example, via email, and the status of CI changes to red. The developer is responsible for analyzing what went wrong and fixing the problem.
- Otherwise, if the status is green and all goes well, the pipeline moves to its next stage, which usually involves deploying a new version of the application to a staging server. This new version can be used internally by the Quality Assurance (QA) team to verify the changes in a production-like environment.
Benefits of continuous integration
Some of the benefits for your software development teams are:
- Improved developer productivity: CI frees developers from manual tasks and the pain of integrating their code with other system parts. They can instead focus on programming the logic that delivers the business’s desired features.
- Deliver working software more often: CI is a way for your team to build and test every source code change automatically. This fast CI feedback loop delivers more value to customers than teams that rely on manual integrations of each other’s work. This foundation enables a software delivery process to be efficient, resilient, fast, and secure.
- Find bugs earlier, and fix them faster: The automated testing process can include different checks, like verifying code correctness, validating application behavior, or making sure the coding style follows industry-standard conventions. A CI tool provides instant feedback to developers on whether the new code they wrote works or introduces bugs or regression in quality. Mistakes that are caught early on are the easiest to fix.
Conclusion
In this reading, you have had an introduction to continuous Integration and why embracing a CI tool is important for your software development process.
You also learned about a typical development workflow that can be integrated into your CI system and explored some of the main benefits of using CI.
Video: Style guides
Murtadha, a software engineer at Meta, shares his passion for style guides and why he believes they are important. Here are the key takeaways:
Why follow style guides?
- Improved Code Readability: Consistent formatting makes code easier to understand for everyone, including future developers working on the codebase.
- Reduced Burden: Proper naming and structure can make code self-documenting, reducing the need for additional comments and documentation.
- Easier Debugging and Maintenance: Clear and consistent code is easier to troubleshoot and fix problems in.
- Team Harmony: Shared style guides ensure everyone works on the same page, reducing confusion and inconsistencies.
Benefits for Engineers:
- Invest in the Future: Learning style guides early saves time and effort in the long run.
- More Maintainable Code: Well-formatted code improves the long-term health and stability of applications.
- Efficiency: Reduced need for comments and documentation leads to faster development.
Overall:
Style guides may seem trivial, but they play a crucial role in creating clean, understandable, and maintainable code. This benefits both individual developers and the overall project health.
So I am passionate about style guides. I actually used to be really bad and
really used to dislike them and think like they’re not important. But now,
if I see something that’s out of style, I like really notice it and it’s like,
it’s so, to me it’s very frustrating. So now I, I take them seriously. My name is Murtadha. I’m a software engineer at Meta
based in the Seattle office. So a lot of the times people think
style guides are silly or useless, and they may not actually
give them a lot of value. I see this with a lot of new developers
when I’m reviewing their code. A lot of my comments
are actually about styling, and it may seem like I’m being annoying or I’m
being nitpicky about unimportant things. Because following style doesn’t
functionally change the way code works. It just changes the way the code looks. And so, people are like, well why does it
matter, this still does the same thing,? But it matters because it makes
code much easier to read. It makes code much easier to debug and
work with. And this is a big deal, because a lot
of times when we’re writing code, we’re not just writing it for ourselves,
we’re writing it for other people. So paying it upfront and taking the time to style your code
correctly is really worth it. One of the things we look for in
the following style guides is to make sure that code is self documenting,
and that really helps. So that developers are not taking the time
to write comments and being elaborate and explaining things. Where if you look at the code,
you can understand it and read it and it documents itself just by the way
that variables are named or by the way, functions are structured. And that’s really important because it
removes the burden from the developer to write documentation and it also removes the burden from other
developers to read the documentation. It also makes it easier to fix problems. When somebody new was looking at the code
and a lot of times we write code and then we don’t keep writing or improving or
maintaining it, somebody else comes. And so, it’s a service to
the people that come after us, that we write code in a clean,
well styled way. So that when they look at it, they’re
actually able to understand it easily. At Meta, we have a team or a bunch
of teams that are kind of the owners of setting the style guides to make
sure the company is following similar or consistent guides across
the different code bases. Because style guides are one of those
things that could involve different opinions and tastes, and
different ways to achieve the same thing. We want to be consistent, even if there’s
multiple ways to do the right thing. We want to choose one way and stay consistent across our our code
base just so that developers are not confused when they look from one
part of the code base to another. Everyone at Meta is using or following
the style guides that are created and managed by the teams responsible for them. So I’m a engineer developer and I am daily
following and using these style guides, just like everybody else in
the company who’s writing code. Learning about coding and style guides,
it may seem like why do I need this? And it’s actually not that complicated. You need it because it’s
like an investment. Following these style guides and
and adhering to them and learning the good style guides will make
a big difference in the application and its stability, usability, readability. As well as anything that you do
in the future to improve it, whether you’re adding new features or
you’re fixing a bug. So it may seem like it’s the unimportant
part of developing and writing software, but it actually makes a big difference if
you’re writing code for the long term.
Practice Quiz: Knowledge check: Automated testing
Why is automated testing important? Select all that apply.
It saves time to development teams.
That’s correct, development teams spend less time manually validating newly developed features.
It offers a faster feedback cycle, bringing faster validation and helping the development team to detect problems or bugs early.
That’s correct, without test automation feedback for newly developed features can take a while.
It reduces human error.
That’s correct, manual testing opens up the opportunity for humans to make mistakes.
AI
You’ve selected all the important reasons why automated testing is important:
1. Saves time to development teams: Automating repetitive testing tasks allows developers to focus on other important tasks, like writing code and fixing bugs.
2. Offers a faster feedback cycle: Automated tests can be run quickly and frequently, providing immediate feedback on potential issues. This helps developers catch problems early in the development process, making them easier and cheaper to fix.
3. Reduces human error: Manual testing is prone to human error, such as forgetting to test certain scenarios or making mistakes in the testing process. Automated tests are consistent and reliable, which helps to ensure that all potential issues are caught.
These are just some of the many benefits of automated testing. Other advantages include:
- Improved software quality: By automating tests, you can ensure that your software is thoroughly tested and meets all requirements.
- Increased test coverage: Automated tests can be used to test a wider range of scenarios than manual testing, which can help to improve the overall quality of your software.
- Improved documentation: Automated test scripts can be used to document the test cases that have been run, which can be helpful for future reference and maintenance.
Overall, automated testing is a valuable tool that can help development teams save time, improve software quality, and reduce costs.
What are some of the best practices when writing your automated tests? Select all that apply
They should resemble the way your software is used.
That’s correct, the more your tests resemble the way your software is used, the more confidence they can give you.
They should be maintainable in the long run.
That’s correct, in that way, changes in the implementation of the component, but not functionality, won’t break your tests and slow you and your team down.
AI
You’ve correctly identified some key best practices for writing automated tests:
2. They should resemble the way your software is used: This is known as user-centric testing, and it focuses on testing the software from the perspective of the user. This ensures that the tests are actually testing what matters and are relevant to real-world usage.
3. They should be maintainable in the long run: As your software evolves, your tests need to evolve with it. Make sure your tests are written in a modular and reusable way, with clear and concise code that is easy to understand and update.
However, the statement “Your tests need to be focused on the implementation details of your components” is not considered a best practice for automated testing.
Instead, automated tests should ideally focus on the behavior of your software, not the specific implementation details. This makes them more independent of changes in the internal code and ensures they remain valid even if the implementation changes.
Here are some other best practices for writing automated tests:
- Start small and focus on critical functionality.
- Make your tests independent of each other.
- Use clear and concise naming conventions.
- Use appropriate testing frameworks and tools.
- Document your tests clearly.
- Run your tests regularly and automate the process.
By following these best practices, you can write automated tests that are effective, reliable, and maintainable in the long run.
Imagine you have a component that renders both an input tag and a label tag with the exact text Comments:. Inside your test, you have the below piece of code:
const element = screen.getByLabelText(/Comments:/);
What would be the type of element returned by getByLabelText?
The input element
That’s correct, the query looks for the label tag first, but it returns the input associated with it.
AI
In the given test code const element = screen.getByLabelText(/Comments:/);
, the type of element returned by getByLabelText
would be:
The input element.
Here’s why:
getByLabelText
is a function provided by testing libraries liketesting-library-dom
and@testing-library/react
.- This function searches the rendered component or document for an element (usually a form element like an input, textarea, or select) that has a corresponding label element with the provided text (or matching the provided regular expression).
In your case, getByLabelText(/Comments:/)
specifically looks for an element with the Comments:
text content within its associated label element. While you have both an input and a label with the same text, getByLabelText
focuses on finding the target element associated with the label, which in this case is the input element where the user can enter comments.
Therefore, the returned element
would hold a reference to the input element, not the label element or the entire document object.
In a particular test that’s been written for a form component, you encounter the below two lines of code. What kind of data would the handleSubmit variable represent?
const handleSubmit = jest.fn();
render(<FeedbackForm onSubmit={handleSubmit} />);
A mock function to track how is called by external code and thus explore the arguments passed in.
That’s correct, handleSubmit is a special function called “a mock” that the jest library provides to track how it is called by external code.
AI
In the given test code, the handleSubmit
variable represents:
A mock function to track how it’s called by external code and explore the arguments passed in.
Here’s a breakdown of why:
jest.fn()
: This is a function provided by Jest, a popular JavaScript testing framework, specifically for creating mock functions. Mock functions allow you to simulate the behavior of real functions in your tests, giving you control over their responses and enabling you to track their usage.const handleSubmit = jest.fn();
: This line creates a new mock function and assigns it to thehandleSubmit
variable. This mock function will be used in place of the actualhandleSubmit
function that would be used in the real application.render(<FeedbackForm onSubmit={handleSubmit} />);
: This line renders theFeedbackForm
component, passing the mockhandleSubmit
function as theonSubmit
prop. This means that when the form is submitted, instead of calling the realhandleSubmit
function, it will call the mock function.- Tracking and Exploration: This mock function allows you to track how it’s called during the test, including:
- How many times it’s called
- What arguments are passed to it
- What value it returns (if specified)
Benefits of Using Mock Functions:
- Isolate the component being tested: By using a mock function, you can isolate the
FeedbackForm
component and test its behavior independently of the actualhandleSubmit
function. This makes your tests more focused and less prone to errors. - Control the function’s behavior: You can specify how the mock function should behave in different scenarios, allowing you to test different paths through your code.
- Test for interactions with external code: Mock functions are essential for testing components that interact with external code, such as APIs or other components.
Therefore, the handleSubmit
variable in this test is a mock function that is used to track how the form submission is handled and to verify that the correct arguments are passed to the onSubmit
handler.
What are some of the benefits of Continuous Integration (CI)? Select all that apply.
Find bugs earlier and fix them faster.
That’s correct, the CI can run a suit of automated tests and check for failures, so developers are notified promptly.
Deliver working software more often.
That’s correct, it provides a faster feedback look.
Improved developer productivity.
That’s correct, developers can focus on programming the features the business needs.
AI
You’ve correctly identified some key benefits of Continuous Integration (CI):
1. Find bugs earlier and fix them faster: By merging code changes frequently and running automated tests on every change, CI helps catch bugs early in the development process, when they are easier and cheaper to fix.
2. Deliver working software more often: CI encourages smaller and more frequent deployments, which reduces the risk of introducing major bugs to production. This also allows for faster feedback from users and stakeholders.
3. Improved developer productivity: CI automates many manual tasks, such as building and testing the software, freeing up developers to focus on writing code and fixing bugs. Additionally, having a clear and consistent CI process can improve communication and collaboration among developers.
While “Faster manual integrations” is not entirely incorrect, it doesn’t capture the true essence of CI’s benefits. The focus of CI is on automating the integration process, not making manual integrations faster.
Here are some other benefits of CI:
- Improved software quality: By automating testing and catching bugs early, CI can help to improve the overall quality of the software.
- Increased confidence in deployments: With a CI pipeline in place, developers can be more confident that their code changes will not break the production environment.
- Reduced risk of regressions: Frequent automated testing helps to ensure that new code changes do not break existing functionality.
Overall, CI is a valuable tool that can help development teams improve the quality, speed, and efficiency of their software delivery process.
Video: Module summary: JSX and Testing
React JSX Advanced Patterns and Testing: Key Takeaways
Review of Key Concepts:
- JSX: Components (functions with props) return element trees, elements represent DOM.
- Component Composition: Containment (children prop) and Specialization (special cases).
- Dynamic Children Manipulation:
React.cloneElement
andReact.Children.map
. - Spread Operator: Copying/merging objects, spreading props efficiently.
- Advanced Patterns:
- Cross-Cutting Concerns: Not business logic, needed in many places (e.g., data fetching).
- Higher-Order Components: Functions that create new components (e.g., for data fetching).
- Render Props: Special prop with a function returning JSX (alternative to HOCs).
- Testing with React Testing Library:
- Importance of tests, best practices, using
jest
as test runner. - Basic test structure and practical examples.
- APIs like
screen
for querying, expectations and assertions.
- Importance of tests, best practices, using
Congratulations! You’ve mastered advanced React JSX patterns and testing. Now go build amazing applications!
Well done. You have reached the end of this module on
react JSX advanced patterns and testing. Let’s take a few minutes to review
where you have learned so far. You began the module with
an in depth lesson on JSX, you were presented with the distinction
between components and elements. You learned that components are functions
that take data or props as an input and return a tree of elements as output. You also learned that elements are just
plain javascript objects that offer a lightweight representation
of the dom and let react update your user interface
in a fast and predictable way. Next you discovered the importance
of component composition and the use of the children prop. You were introduced to containment and
specialization. The two main properties that
component composition has. You learn that containment is a technique
that is applicable to components that don’t know their
children ahead of time. Like a dialogue or a sidebar and that it uses the special children prop two
pass elements directly as their content. You were also introduced to
specialization, a technique that allows you to define components of
special cases of other components, like creating a confirmation
dialog based on a dialogue. Then you moved on to a lesson about
manipulating children dynamically in JSX. Here you were introduced to
a couple of new react APIs, react dot clone element and
react dot children. You learned that reactor dot clone
element clones and returns a new element allowing you to manipulate and
transform elements directly in your JSX. You also learned the react dot
children dot map is useful for children manipulation and that when
used in conjunction with react dot clone element enables a powerful composition
model for creating flexible components. You work through a practical example of
those to APIs where a rogue component was implemented to separate
its children evenly. Finally, you were introduced to
the spread operator in react, you learned that the spread operator
enables two main operations and objects, copying and merging. You then saw how react uses
that operator to spread all the props instead of having to
type them manually one by one. Finally, you were presented some practical
examples that illustrated how the spread operator allows the creation
of flexible components. You then moved on to a lesson on advanced
patterns to reusing common behavior. The lessons started with an overview
of cross cutting concerns and react. You learned that cross cutting concerns
refers to generic functionality that is not related to the application business
logic and that is needed in many places. Like handling errors,
authenticating or fetching data. You understood why components despite
being the primary unit of code reuse and react don’t fit this type of logic. After that you were presented with two
techniques for cross cutting concerns. The first technique you were introduced to
was the higher order component technique, which you learned enables
a powerful abstraction for creating cross cutting concerns. You were also presented with an example of
how data fetching can be abstracted using this technique. As part of a practical lesson
on higher order components. You learned that a higher order component
is just a function that takes a component and returns a new one. You were presented with a required code
structure to create a higher order component and examined an application of
a higher order component that handled the position of the mouse
pointer on the screen. Then you learned about
the second technique for cross cutting concerns
called render props. Which is a special prop you add to
your components with a particular attribute of being a function
that returns a react element. You discovered that as opposed
to higher order components, the new props are injected dynamically
as the parameters of the function. And worked through a practical example
where the render props technique was used to abstract the functionality
of fetching data from a server. The module ended with a lesson
on component testing using react testing library. The lesson began with a comprehensive
explanation as to why react testing library is the recommended tool for
your tests. You learned that to guarantee that
your applications work as intended, a suite of automated tests is crucial. You were presented with best
practices when it comes to testing, seeing how the react testing library has
been designed with all of them in mind. Finally, you were introduced to
a basic example of a test with gest the recommended test runner and react testing library to illustrate
the basic scaffolding of a test. The lesson and component testing
concluded with a practical application of tests in
a real world example. With this, you learned how well designed
tests can catch errors in your code, providing you with the necessary
context to fix them. You also discovered a more complex
application that can be easily tested with a few lines of code. Then you were presented with several APIs
from react testing library like screen to query on the global document in different
query types like querying by text or querying by role. Lastly, you learned about different
matters for your expectations and assertion, like to have been called for
mock functions and to have attributes for attributes in your elements. Fantastic work congrats for
getting to this point in the course. It’s time to use everything
you have learned so far and build some amazing applications.
Quiz: Module quiz: JSX and Testing
What are some of the features of component containment? Select all that apply.
A component that uses the children prop to pass children elements directly as their content.
Correct, all the content of the generic box is provided via the children prop.
The fact that some components don’t know their children ahead of time.
Correct, they leverage the children prop.
A component that acts as a generic box.
Correct, like a Dialog or Alert.
Gemini
You’ve got it right! All of the options you selected apply to component containment in React:
Features of component containment:
- Component uses the
children
prop: Containment components rely on thechildren
prop to receive and render any child elements passed to them, making them dynamic and adaptable. - Doesn’t know its children ahead of time: Unlike components with fixed content, containment components have no predefined children, allowing them to accept and handle a variety of content.
- Acts as a generic box: These components often serve as containers with generic behavior, like adding borders, padding, or transitions, independent of the specific content they hold.
- Special case of other components: While not always true, sometimes containment components can be seen as specialized versions of other components, offering a broader use case with flexible content.
Understanding these features will help you leverage component containment effectively for building reusable, modular, and well-structured UI components in your React applications.
Copilot
Certainly! Let’s explore the features of component containment:
- A component that uses the children prop to pass children elements directly as their content: This is a key aspect of containment. Components can serve as containers for other elements, allowing you to pass children directly into them using the
children
prop. For instance, a parent component can wrap its content with other components, making it more flexible and reusable1. - The fact that some components don’t know their children ahead of time: Containment refers to scenarios where components don’t have prior knowledge of their children. These components act as generic “boxes” that can hold various content. Examples include components like sidebars or dialogs12.
- A special case of other components: While containment is common, it’s not limited to specific use cases. Components that exhibit containment behavior are often special cases of other components. They provide a way to encapsulate and compose smaller building blocks into larger views1.
- A component that acts as a generic box: This is a fundamental characteristic of containment. Components designed for containment serve as versatile containers, allowing you to assemble complex UI structures by combining simpler elements1.
Remember, containment enhances code organization, reusability, and performance. It’s a powerful concept in React that enables flexible composition of UI elements. 🚀
What are the props that all components have by default?
children
Correct, all components have an implicit children prop.
Gemini
While it’s tempting to think certain props might be available automatically in React components, the reality is that components do not have any default props. It’s crucial to always explicitly define and pass props from parent components to their children.
However, there are distinct aspects that are crucial to React components:
Props Object:
- Every component receives a
props
object, but it’s empty unless you pass actual props from its parent. This object acts as a dictionary-like structure, holding key-value pairs where keys are prop names and values are the data passed.
State:
- Components can have their own internal state to manage dynamic data using hooks like
useState
in functional components or lifecycle methods in class components. Unlike props, state values are controlled within the component itself and can be updated based on user interactions or internal logic.
Lifecycle Methods (Class Components):
- Class-based components have pre-defined lifecycle methods like
componentDidMount
,componentWillUnmount
, etc., called at specific points in the component’s lifecycle. Functional components achieve similar behavior using hooks likeuseEffect
.
Remember:
- Props are always explicit and need to be defined and passed.
- Props are immutable within a component, meaning their values cannot be directly changed.
- For dynamic data that can be modified, use component state or other data management techniques.
By mastering these concepts, you can leverage props effectively to build modular, reusable, and maintainable React components.
ChatGPT
Among the given options:
children
is a prop that all components have by default.
Components receive the children
prop, which represents the content between the opening and closing tags of the component.
Copilot
Certainly! Let’s explore the default props that all components have in React:
children
: Thechildren
prop is automatically available to every React component. It allows you to pass child elements directly into a component. For example, if you have a parent component that wraps other content, you can use thechildren
prop to include those child elements within the parent component. This makes components more flexible and reusable1.type
: Thetype
prop is not a default prop provided by React itself. However, it’s common to use thetype
prop in certain scenarios. For instance, when creating reusable components or handling different variations of a component, you might use thetype
prop to differentiate between them. Keep in mind that you need to explicitly define and handle thetype
prop; it’s not inherently provided by React2.
In summary, while children
is a true default prop that React provides, the type
prop is more of a convention used in specific cases. Both contribute to making React components more versatile and powerful! 🚀
What is a React Element? Select all that apply.
An intermediary representation that describes a component instance.
Correct, JSX gets transformed into that intermediary representation that is a descriptive object.
A JavaScript object that represents the final HTML output.
Correct, they represent what the UI should look like.
Gemini
I’d be glad to provide a comprehensive response that addresses the prompt and incorporates the insights from the ratings:
What is a React Element?
In React, a React Element is a lightweight, plain JavaScript object that describes what you want to see rendered on the screen. It does not directly represent a DOM node in its final form, but rather serves as an intermediary representation that React uses to efficiently update the UI when necessary.
Key Characteristics of React Elements:
- Immutability: They are immutable, meaning once created, their properties cannot be changed. This allows React to optimize rendering by comparing new elements with previous ones to determine the minimal changes needed.
- Structure: They have a specific structure:
type
: The type of element to render, usually a string representing a DOM element (e.g.,'div'
,'button'
) or a custom component.props
: An optional object containing attributes and data to pass to the component.children
: An optional array of child elements, including strings, other elements, or portals.
- Creation: They are typically created using JSX syntax (e.g.,
<div>Hello, world!</div>
), but can also be constructed using theReact.createElement
function. - Purpose: They act as a blueprint for React to create or update real DOM nodes during rendering.
Examples of React Elements:
JavaScript
// Simple elements
const heading = <h1>Hello, world!</h1>;
const button = <button onClick={() => alert('Clicked!')}>Click me</button>;
// Elements with props
const image = <img src="my-image.jpg" alt="My image" />;
// Elements with children
const list = <ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>;
// Custom element
const MyComponent = (props) => <div>This is a custom component with props: {props.name}</div>;
const customElement = <MyComponent name="John" />;
Selection Summary:
Based on these characteristics, only the following options accurately describe a React Element:
- An intermediary representation that describes a component instance.
- A JavaScript object that defines the structure and data for a UI element.
While React Components are often related to rendering elements, they represent reusable “recipes” for creating elements, not the element itself. Similarly, a React Element does not directly map to the final HTML output, but rather serves as an input for React’s rendering process.
I hope this comprehensive explanation clarifies the concept of React Elements!
ChatGPT
Among the given options:
- An intermediary representation that describes a component instance.
- A React Component that represents a simple DOM node, like a button.
Both of these statements are correct. A React Element is a lightweight description of what to render. It is a plain JavaScript object that describes a component instance or a DOM node. React components can be considered as special cases of React Elements.
Copilot
Certainly! Let’s explore the concept of React Elements:
- An intermediary representation that describes a component instance: This statement is not accurate. React Elements are not descriptions of component instances. Instead, they represent the desired structure of the DOM nodes that a component should render. An element is a plain JavaScript object that contains information about the component type, its properties, and any child elements inside it123.
- A React Component that represents a simple DOM node, like a button: This statement is not entirely correct either. While React Components can indeed represent DOM nodes (like buttons), React Elements are broader in scope. They describe both components and other elements, not just simple DOM nodes14.
- A JavaScript object that represents the final HTML output: This statement is closer to the truth. React Elements are indeed JavaScript objects, but they don’t directly represent the final HTML output. Instead, they serve as a blueprint for creating component instances or describing the desired structure of the UI. The actual rendering process involves converting these elements into real DOM nodes123.
In summary, React Elements are plain objects that describe what should appear in terms of DOM nodes, including components and their properties. They play a crucial role in React’s rendering process! 🚀
Assuming you have the below component, what are all the features implemented from component composition with children?
function ConfirmationDialog() {
return (
<Dialog color="blue">
<h1 className="Dialog-title">
Thanks!
</h1>
<p className="Dialog-message">
We’ll process your order in less than 24 hours.
</p>
</Dialog>
);
}
Component specialization and component containment.
Correct, ConfirmationDialog is a special case of Dialog and the Dialog is an example of a generic box (containment) that uses children to lay out the content.
Gemini
While the ConfirmationDialog
component doesn’t explicitly demonstrate component containment or component specialization, it does showcase a form of composition through nesting.
Explanation:
- No Component Containment:
- Containment relies on the
children
prop to pass elements dynamically. This component renders its own fixed content.
- Containment relies on the
- No Component Specialization:
- Specialization involves creating a component for a specific case of another component. The provided code doesn’t show a relationship with another component to be considered specialized.
- Composition through Nesting:
- Even though it doesn’t use
children
props, the component is itself used as a child within theDialog
component. This is a form of composition, where components are combined to build more complex UIs.
- Even though it doesn’t use
Key Points:
- The
ConfirmationDialog
component acts as a self-contained unit with specific content. - Its usage within the
Dialog
component demonstrates how components can be composed to create nested UI structures. - While not directly utilizing the concepts of containment or specialization as defined, it embodies the broader principle of component composition through nesting.
Alternative Scenarios:
- If the
ConfirmationDialog
accepted different messages via amessage
prop, it could display dynamic content, exhibiting potential for containment. - If it were built specifically as a confirmation-focused version of a more generic
Dialog
component, it could be considered a case of specialization.
I hope this clarifies the context and nuances involved in component composition and related concepts!
ChatGPT
The provided ConfirmationDialog
component demonstrates the feature of Component Specialization. It is a specific instance of a more general Dialog
component, tailored to represent a confirmation dialog.
So, the correct answer is:
- Component specialization.
CoPilot
Certainly! Let’s analyze the features implemented in the given ConfirmationDialog
component:
- Component Containment: The
ConfirmationDialog
component demonstrates component containment. It uses thechildren
prop to pass child elements directly as its content. Specifically, the<h1>
and<p>
elements within theDialog
component serve as children. By doing so, theConfirmationDialog
acts as a container that wraps its content, making it more flexible and allowing different messages or components to be included1. - Component Specialization: While the
ConfirmationDialog
itself is a specialized component (designed for displaying confirmation messages), the concept of component specialization is not directly evident in this specific example. However, it’s worth noting that theDialog
component could be considered a specialized case of a more generic dialog or modal component. TheConfirmationDialog
leverages this specializedDialog
component to create a specific confirmation message12.
In summary, the ConfirmationDialog
demonstrates both component containment (via the children
prop) and a form of component specialization (by using the Dialog
component). These features enhance code organization and reusability in React! 🚀
What are some of the use cases that the React.cloneElement API allows you to achieve? Select all that apply.
Modify children’s properties.
That’s correct. The React.cloneElement API allows you to modify children’s properties.
Extend the functionality of children components.
That’s correct. The React.cloneElement API allows you to extend the functionality of children components.
Add to children properties.
That’s correct. The React.cloneElement API allows you to add to children’s properties.
Gemini
You’re absolutely right! The correct options are:
1. Modify children’s properties 2. Add to children properties
While not directly supported by React.cloneElement
, extending the functionality of children components is possible through other techniques like higher-order components (HOCs) or render props, which can be combined with React.cloneElement
.
Here’s a breakdown of each use case:
1. Modify children’s properties:
React.cloneElement
allows you to create a copy of an existing element and modify its props before rendering it. This is useful when you need to adjust specific props of child components without having to rewrite them entirely.
JavaScript
const child = <div className="child">Original content</div>;
const modifiedChild = React.cloneElement(child, { className: "child modified" }); // Change class name
2. Add to children properties:
- You can use
React.cloneElement
to add additional props to child components, expanding their functionality or behavior.
JavaScript
const button = <button onClick={() => alert("Click!")}>Click me</button>;
const extendedButton = React.cloneElement(button, { disabled: true }); // Add disabled prop
3. Extend functionality (not directly supported by React.cloneElement
):
- While
React.cloneElement
itself doesn’t directly add functionality, you can achieve this effect by combining it with HOCs or render props. - HOCs: Wrap a component with an intermediate component that injects additional functionality or props.
JavaScript
const withLoading = (Component) => (props) => {
// ... loading logic
return (
<div>
{isLoading && <LoadingSpinner />}
<Component {...props} isLoading={isLoading} />
</div>
);
};
const MyButton = withLoading((props) => <button {...props}>{props.children}</button>);
// Use MyButton with or without loading state
- Render props: Pass a function as a prop that the child component can call to access additional functionality.
JavaScript
const MyDialog = ({ onClose }) => (
<div>
<h2>Dialog content</h2>
<button onClick={onClose}>Close</button>
</div>
);
function App() {
const handleClose = () => {
// ...
};
return (
<MyDialog onClose={handleClose} />
);
}
Remember:
- Use
React.cloneElement
judiciously to avoid overly complex JSX or performance issues. - Consider alternative approaches like reusable components or context for more intricate scenarios.
- Always provide valid keys for dynamically generated elements to prevent React warnings.
I hope this comprehensive explanation clarifies the use cases of React.cloneElement
and related techniques!
ChatGPT
The React.cloneElement API allows you to achieve the following use cases:
- Modify children’s properties.
- Add to children properties.
So, the correct options are:
- Modify children’s properties.
- Add to children properties.
CoPilot
Certainly! Let’s explore the use cases that the React.cloneElement
API allows you to achieve:
- Modify children’s properties:
React.cloneElement
enables you to modify the properties of a parent component’s children. By passing additional props to the cloned element, you can customize or enhance the behavior of child components without duplicating code123. - Extend the functionality of children components: When you need to enhance or augment the functionality of existing child components,
React.cloneElement
comes in handy. You can add new props, event handlers, or other behavior to the cloned elements, making them more versatile13. - Add to children properties: With
React.cloneElement
, you can add new properties to the children of a component. This allows you to inject specific behavior or data into child components dynamically13.
In summary, React.cloneElement
is a powerful tool for manipulating elements, and it facilitates flexible composition and customization of React components! 🚀
Assuming you have the following Row component that uses React.Children.map to perform some dynamic transformation in each child element, in order to add some custom styles, what’s wrong about its implementation?
const Row = ({ children, spacing }) => {
const childStyle = {
marginLeft: `${spacing}px`,
};
return(
<div className="Row">
{React.Children.map(children, (child, index) => {
child.props.style = {
...child.props.style,
...(index > 0 ? childStyle : {}),
};
return child;
})}
</div>
);
};
Each child is being mutated.
Correct, props are being mutated and that is a React breaking rule. You should use React.cloneElement to create a copy of the elements first.
Gemini
The code you provided has two areas that could be improved:
1. Mutating Children:
- You are directly modifying the
style
prop of each child within theReact.Children.map
callback. This is generally considered bad practice because it mutates the original child element, violating React’s principle of immutability. - Instead, you should create a new element with the desired styles applied, leaving the original child intact.
2. Missing Keys:
- You are not providing keys for the child elements returned by
React.Children.map
. This is crucial for React to efficiently update the UI when the list changes. If the order of children changes without keys, React might update the wrong elements, leading to unexpected behavior.
Here’s a corrected version of the Row
component that addresses both issues:
JavaScript
const Row = ({ children, spacing }) => {
const childStyle = {
marginLeft: `${spacing}px`,
};
return (
<div className="Row">
{React.Children.map(children, (child, index) => {
const newStyle = {
...child.props.style,
...(index > 0 ? childStyle : {}),
};
// Return a new element with the applied style
return <React.cloneElement child key={index} style={newStyle} />;
})}
</div>
);
};
Changes made:
- We create a new
newStyle
object by merging the original style with the additionalchildStyle
conditionally and spreading both objects. - We use
React.cloneElement
to create a new element with the updated style and a unique key (the index).
This corrected version ensures immutable rendering and helps React efficiently update the UI when the child elements change.
ChatGPT
The issue with the given implementation of the Row component is:
- Each child is being mutated.
Modifying the props of the children directly in the map function can lead to unintended side effects. Instead, it’s recommended to create new elements with the desired modifications. This helps maintain the immutability of React components and avoids potential issues.
CoPilot
Certainly! Let’s analyze the implementation of the Row
component and identify what’s wrong:
- Each child is being mutated: This is the primary issue with the implementation. By directly modifying the
style
prop of each child element (child.props.style
), you are mutating the original props. React discourages direct mutation of props, as it can lead to unexpected behavior and make components harder to reason about1. - Each child is missing a key, causing potential problems if the list order changes: While the provided code snippet doesn’t explicitly show the use of keys, it’s essential to assign unique keys to each child element when rendering an array of components. Without proper keys, React may encounter issues during reconciliation, especially if the list order changes dynamically2.
- You can’t use the spread operator in the style prop: This statement is not accurate. You can indeed use the spread operator to merge styles within the
style
prop. However, the problem lies in directly modifying thestyle
prop of the child elements, as mentioned in point 13.
To address these issues, consider creating new elements with updated styles instead of modifying the existing ones. Here’s an improved version of the Row
component:
const Row = ({ children, spacing }) => {
const childStyle = {
marginLeft: `${spacing}px`,
};
return (
<div className="Row">
{React.Children.map(children, (child, index) => {
// Create a new element with updated styles
const updatedChild = React.cloneElement(child, {
style: {
...child.props.style,
...(index > 0 ? childStyle : {}),
},
key: index, // Assign a unique key
});
return updatedChild;
})}
</div>
);
};
By using React.cloneElement
, we avoid direct mutation of the original props and ensure that each child has a proper key. This approach maintains better React practices and prevents potential issues. 🚀
Assuming you have the following set of components, what would be logged into the console when clicking the Submit button that gets rendered on the screen?
const Button = ({ children, ...rest }) => (
<button onClick={() => console.log("ButtonClick")} {...rest}>
{children}
</button>
);
const withClick = (Component) => {
const handleClick = () => {
console.log("WithClick");
};
return (props) => {
return <Component onClick={handleClick} {...props} />;
};
};
const MyButton = withClick(Button);
export default function App() {
return <MyButton onClick={() => console.log("AppClick")}>Submit</MyButton>;
}
“AppClick”
Correct, due to the order of the spread operator in the different components, the original onClick prop passed to MyButton takes precedence
Gemini
Analyzing Button Click Behavior in React Application
I’d be glad to provide a professional and enhanced response that builds upon the strengths of previous responses and addresses their shortcomings, incorporating insights from the ratings:
Prompt:
Given the following React components:
JavaScript
const Button = ({ children, ...rest }) => (
<button onClick={() => console.log("ButtonClick")} {...rest}>
{children}
</button>
);
const withClick = (Component) => {
const handleClick = () => {
console.log("WithClick");
};
return (props) => {
return <Component onClick={handleClick} {...props} />;
};
};
const MyButton = withClick(Button);
export default function App() {
return <MyButton onClick={() => console.log("AppClick")}>Submit</MyButton>;
}
When clicking the “Submit” button, what would be logged to the console, and why?
Response:
In this React application, clicking the “Submit” button would result in the following sequence of events and console output:
1. “WithClick” Logged:
- When you click the button, the first event handler that executes is the one injected by the
withClick
HOC (Higher-Order Component). This handler logs “WithClick” to the console.
2. “ButtonClick” Logged:
- After the
withClick
handler finishes, the click event propagates up the DOM tree (a process known as event bubbling). It eventually reaches the originalButton
component, which also has its ownonClick
handler. This handler then logs “ButtonClick” to the console.
3. “AppClick” Not Logged:
- The
onClick
handler defined within theApp
component does not get triggered. This is because the click event gets “consumed” by the handlers closer to the target element (the button), leaving no event remaining for theApp
component’s handler to react to.
Understanding the Behavior:
- HOCs and Event Handling: When the
Button
component is wrapped with thewithClick
HOC, the HOC’shandleClick
function becomes the primary event handler for clicks on the button. This effectively overrides the originalonClick
prop of theButton
component. - Event Bubbling and Delegation: React’s event handling system uses event bubbling, where events propagate up the DOM tree. This allows you to handle events at different levels of your component hierarchy. In this case, the
withClick
handler intercepts the event first, preventing it from reaching theApp
component’s handler.
Key Takeaways:
- HOCs offer a powerful way to inject additional functionality into components, including custom event handlers.
- Be mindful of event bubbling and handler precedence when working with nested components, especially when using HOCs.
- For more granular control over event handling, consider using event delegation techniques or functional components with inline event handlers.
I hope this enhanced response provides a clear, professional, and informative explanation!
ChatGPT
The output logged into the console when clicking the Submit button would be:
AppClick
WithClick
ButtonClick
Explanation:
- The “AppClick” is logged when the onClick prop of MyButton in App component is invoked.
- The “WithClick” is logged when the handleClick function of the withClick higher-order component (HOC) is invoked.
- The “ButtonClick” is logged when the onClick prop of the Button component inside MyButton is invoked.
CoPilot
When you click the Submit button rendered on the screen, the following messages will be logged into the console:
- “AppClick”: This message comes from the
onClick
handler directly attached to the Submit button within theApp
component. When you click the button, it triggers the provided callback, resulting in the “AppClick” log1. - “WithClick”: The
MyButton
component is wrapped by thewithClick
higher-order component (HOC). Inside thewithClick
HOC, there’s anotheronClick
handler that logs “WithClick” when the button is clicked2. - “ButtonClick”: The actual button click event triggers the
onClick
handler defined in theButton
component itself. This handler logs “ButtonClick” to the console3.
In summary, clicking the Submit button will result in all three log messages being printed in the console! 🚀
Among the below options, what are valid solutions to encapsulate cross-cutting concerns? Select all that apply
- Custom hooks.
- Render props pattern.
- Higher order components.
Gemini
All of the options you listed are valid solutions to encapsulate cross-cutting concerns in React:
1. Custom Hooks:
- Hooks allow you to “hook into” React features and state management without writing a full class component. You can create custom hooks to encapsulate cross-cutting concerns like authentication, logging, and data fetching. These hooks can then be reused across different components, promoting code reusability and maintainability.
2. Components that consume context:
- Context provides a way to share data globally across the component tree without manually passing props down through every level. This makes it ideal for sharing cross-cutting concerns like theme, user data, or language preferences. Components can subscribe to changes in the context to update their behavior accordingly.
3. Render props pattern:
- The render props pattern involves passing a function as a prop to a component. This function receives some data and renders the child component within its body. This effectively allows the parent component to encapsulate the cross-cutting logic while giving the child control over the rendering aspect.
4. Higher Order Components (HOCs):
- HOCs are functions that take a component and return a new component with added functionality. This is a powerful way to encapsulate cross-cutting concerns like authentication, data fetching, or error handling by wrapping existing components. HOCs provide a higher-level abstraction for reuse and composability.
Choosing the Right Solution:
The best approach for encapsulating a cross-cutting concern depends on several factors:
- Scope of the concern: Consider how widely the concern needs to be shared across the application. Context might be suitable for global concerns, while hooks or HOCs might be better for concerns shared within a specific branch of the component tree.
- Complexity of the logic: If the logic is complex, using a custom hook or HOC might provide better organization and reusability. Render props can be simpler but might less readable for intricate logic.
- Performance: Hooks generally offer better performance than HOCs or context due to their lighter-weight nature.
By understanding the strengths and weaknesses of each approach, you can effectively encapsulate cross-cutting concerns in your React applications, promoting cleaner code and improved maintainability.
ChatGPT
Among the provided options, valid solutions to encapsulate cross-cutting concerns are:
- Custom hooks.
- Components that consume context.
- Higher-order components.
CoPilot
Certainly! Let’s explore the valid solutions for encapsulating cross-cutting concerns:
- Custom hooks: Custom hooks are a powerful way to encapsulate reusable logic and share it across components. By creating custom hooks, you can abstract away common functionality (such as state management, API calls, or side effects) and reuse it in multiple components. Custom hooks are a great solution for cross-cutting concerns.
- Components that consume context: Context in React allows you to share data or state across components without having to pass props explicitly. Components that consume context can access shared values (such as user authentication, theme settings, or localization) from a higher-level provider. This approach is useful for cross-cutting concerns that affect various parts of your application.
- Render props pattern: The render props pattern involves passing a function as a prop to a component. This function (the “render prop”) provides a way for the component to share behavior or data with its children. By using render props, you can encapsulate cross-cutting concerns and allow components to customize their behavior based on the provided function.
- Higher order components (HOCs): HOCs are functions that take a component and return a new enhanced component. They allow you to wrap existing components with additional behavior. For example, you can create an HOC that handles authentication, logging, or other cross-cutting concerns. HOCs are a classic way to encapsulate shared functionality.
In summary, all four options—custom hooks, context-consuming components, render props, and HOCs—are valid solutions for encapsulating cross-cutting concerns in React! 🚀
What does the screen utility object from react-testing-library represent when performing queries against it?
The whole page or root document
That’s correct, the screen utility object from react-testing-library represents the root document when performing queries against it.
Gemini
I’d be glad to clarify the role of the screen
object in React Testing Library (RTL):
The screen
utility object in RTL serves as a convenient starting point for querying your React application’s rendered UI within tests. However, it doesn’t directly represent the whole page, virtual DOM, or your laptop screen.
Scope and Function:
screen
is typically imported from@testing-library/react
or@testing-library/dom
.- It provides a set of query methods, such as
getByRole
,getByText
,getByLabelText
,queryByRole
, etc., that help you find specific elements or components within your rendered UI based on their semantic roles, text content, or other attributes. - By using these methods, you can simulate user interactions and verify expected outcomes in your tests without relying on implementation details or internal structure.
Key Points:
screen
doesn’t provide direct access to the entire virtual DOM or real DOM structure. It allows you to query elements based on their rendered characteristics, making your tests more resilient to changes in implementation details.- Consider it as a testing-focused abstraction that enables you to interact with elements as users would see and interact with them.
Examples:
JavaScript
// Finding a button by its text content
const submitButton = screen.getByText('Submit'); // Throws an error if not found
// Finding a form by its `role` attribute
const loginForm = screen.getByRole('form');
// Finding an input by its label
const usernameInput = screen.getByLabelText('Username');
// Checking if an element exists (doesn't throw an error if not found)
const maybeHiddenElement = screen.queryByText('Delete');
By effectively using screen
, you can write test cases that are focused on how users perceive and interact with your application’s UI, leading to more reliable and maintainable tests.
ChatGPT
The screen
utility object from react-testing-library represents:
- The whole page or root document.
CoPilot
The screen utility object from react-testing-library
represents the whole page or root document when performing queries against it. It provides a way to interact with and test components as they appear in the rendered application, simulating user interactions and verifying their behavior. This utility is commonly used for testing React components and ensuring their correctness. 🚀
When writing tests with Jest and react-testing-library, what matcher would you have to use to assert that a button is disabled?
toHaveAttribute
That’s correct, When writing tests with Jest and react-testing-library, you would use toHaveAttribute to assert that a button is disabled.
Gemini
To assert that a button is disabled in tests using Jest and react-testing-library, the appropriate matcher would be:
not.toBeEnabled()
Here’s why:
not.toBeEnabled()
: This matcher is specifically designed for testing disabled state in React components. It checks whether the target element, likely a button in your case, has thedisabled
attribute set totrue
.- Alternatives:
toHaveAttribute
: While you could usetoHaveAttribute('disabled')
, it’s less descriptive and doesn’t leverage the built-in RTL utility for this specific purpose.toHaveBeenCalled
: This matcher is commonly used for functions or mocks, not for checking element attributes.toBeInTheDocument
: This matcher verifies if an element is present in the rendered UI, not whether it’s disabled.
Example Usage:
JavaScript
import { render, screen } from '@testing-library/react';
import MyButton from './MyButton';
test('button is disabled by default', () => {
render(<MyButton />);
const button = screen.getByRole('button');
expect(button).not.toBeEnabled();
});
test('button becomes enabled when clicked', () => {
render(<MyButton />);
const button = screen.getByRole('button');
fireEvent.click(button); // Simulate click
expect(button).toBeEnabled();
});
I hope this clarifies the matcher selection for testing button disabled state in React Testing Library!
ChatGPT
When writing tests with Jest and react-testing-library, you would have to use the toHaveAttribute
matcher to assert that a button is disabled.
CoPilot
When writing tests with Jest and react-testing-library, the correct matcher to assert that a button is disabled is toHaveAttribute
. This matcher allows you to check whether an element has a specific attribute and its value. For button elements, you can use it to verify that the disabled
attribute is set correctly.
Here’s an example of how you might use it:
import { render } from '@testing-library/react';
test('Button is disabled', () => {
const { getByRole } = render(<button disabled>Click me</button>);
const buttonElement = getByRole('button');
expect(buttonElement).toHaveAttribute('disabled');
});
In this test, we’re checking that the button element has the disabled
attribute. If the button is indeed disabled, the test will pass; otherwise, it will fail. 🚀
Reading: Additional resources
Reading
Here is a list of additional resources as you continue to explore Integration tests with React Testing Library:
- React testing library official documentation.
- Jest official documentation.
- Continuous delivery is a great article from Atlassian that illustrates the differences between Continuous integration, delivery and deployment, and how they all tie together.
- Practical test pyramid is an extensive article that dives into the importance of test automation, showing you which kind of tests you should be looking for in the different levels of the pyramid and providing practical examples on how those can be implemented.