Components
JSX
Props
Rendering
Events
State
Context
useEffect
Routing
TypeScript
State Management
Testing
Forms
...
npx create-next-app@latest
Other Options, see: https://react.dev/learn/start-a-new-react-project
1. Fix the return statement
https://codesandbox.io/s/components-fix-return-f7gfnx
2. Spot the Mistake
https://codesandbox.io/s/components-profile-89h56w
Recommendation: use Named Exports
Export the Card Component
Export the Card component and place it a few times into the App Component. Try it as Default Import / Export and also as Named Import / Export.
https://codesandbox.io/s/galaxus-mini-components-gb0ltp
Solution 💡 https://codesandbox.io/s/galaxus-mini-components-solution-82kz7i
To return multiple elements from a component, wrap them with a single parent tag.
If you don’t want to add an extra <div>
to your markup, you can write <>
and </>
instead:
JSX requires tags to be explicitly closed: self-closing tags like <img>
must become <img />
Converting all these attributes in existing markup can be tedious!
https://transform.tools/html-to-jsx
When you want to pass a string attribute to JSX, you put it in single or double quotes.
If you want to dynamically specify the src
or alt
text, you could use a value from JavaScript by replacing "
and "
with {
and }
:
Any JavaScript expression will work between curly braces, including function calls like formatDate()
:
In addition to strings, numbers, and other JavaScript expressions, you can even pass objects in JSX.
Fix the mistake
Extract some Data
In the <Card>
Component, extract the Image URL, Title, Price and Availability into Variables or an Object. Then use them in the JSX markup using Curly Brackets.
https://codesandbox.io/s/galaxus-mini-components-solution-82kz7i
Solution 💡 https://codesandbox.io/s/galaxus-mini-jsx-solution-jyjv0k
React components use props to communicate with each other.
Every parent component can pass some information to its child components by giving them props.
If you want to give a prop a default value to fall back on when no value is specified, you can do it with destructuring by putting =
and the default value right after the parameter:
Sometimes, passing props gets very repetitive.
In those cases you can use the Spread Operator to forward all your props.
Extract a component
This Gallery
component contains some very similar markup for two profiles. Extract a Profile
component out of it to reduce the duplication. You’ll need to choose what props to pass to it.
https://codesandbox.io/s/props-extract-wnwrwh
Hint: Start by extracting the markup for one of the scientists. Then find the pieces that don’t match it in the second example, and make them configurable by props
A component may receive different props over time.
Props are not always static!
In the following example, the time
prop changes every second, and the color
prop changes when you select another color. And whenever props change, the React Component re-renders
Pass Data down as Props
Move the Card Data (Title, Price etc.) into the App Component and pass it down to the Cards as Props. In the Card component, define default values for each prop.
Bonus: add a new Prop isFavorite
to show the Heart in Pink color (text-pink-400) for certain products.
https://codesandbox.io/s/galaxus-mini-jsx-solution-jyjv0k
Solution 💡 https://codesandbox.io/s/galaxus-mini-props-solution-zu6wmh
Instead of this:
You can write this:
&&
)
Splitting a list in two
This example shows a list of all people. Change it to show two separate lists one after another: Chemists and Everyone Else. You can determine whether a person is a chemist by checking if person.profession === 'chemist'
.
https://codesandbox.io/s/rendering-persons-xfyzw3
Solution 💡 https://codesandbox.io/s/rendering-persons-solution-7ymvgw
Render Cards as a List
Create an Array with different Card Data (Title, Price etc.) for each Card. Loop over the Array to render the Cards. You can find images for your products in the public/images folder.
Bonus: add a new Prop showSizes
to hide the available Sizes on the Card if the product does not have a size (i.e. Tequila)
https://codesandbox.io/s/galaxus-mini-props-solution-zu6wmh
Solution 💡 https://codesandbox.io/s/galaxus-mini-rendering-solution-jhqp0g
React lets you add event handlers to your JSX. Event handlers are your own functions that will be triggered in response to interactions like clicking, focusing and so on.
Instead of passing a function, you can write the Event Handler inline:
Or using an Arrow Function:
Fix an Event handler
Clicking the button is supposed to switch the page background between white and black. However, nothing happens when you click it. Fix the problem. (Don’t worry about the logic inside handleClick
— that part is fine.)
https://codesandbox.io/s/events-toggle-the-lights-gky5xy
Solution 💡 https://codesandbox.io/s/events-toggle-the-lights-solution-wpf9qj
Add Event Listener
In the Card Component, add onClick
Events for the Add to Cart button and the Favorite Button. The Events should just log to the Console (console.log
) what was clicked i.e. Add to Cart: Dom Perignon.
Bonus: add onChange
Event Listener to the Sizes Selection and save which size was selected.
https://codesandbox.io/s/galaxus-mini-rendering-solution-jhqp0g
Solution 💡 https://codesandbox.io/s/galaxus-mini-events-solution-7736rz
Components often need to change what’s on the screen as a result of an interaction.
For example, typing into the form should update the input field. Clicking “next” on an image carousel should change which image is displayed etc.
1. Local variables don’t persist between renders. When React renders this component a second time, it renders it from scratch—it doesn’t consider any changes to the local variables.
2. Changes to local variables won’t trigger renders. React doesn’t realize it needs to render the component again with the new data.
Example https://codesandbox.io/s/state-local-variable-xgkjgz
In React, useState
, as well as any other function starting with ”use
,” is called a Hook.
Complete the gallery
When you press “Next” on the last sculpture, the code crashes. Fix the logic to prevent the crash. You may do this by adding extra logic to event handler or by disabling the button when the action is not possible.
After fixing the crash, add a “Previous” button that shows the previous sculpture. It shouldn’t crash on the first sculpture.
https://codesandbox.io/s/state-gallery-jc8m4m
Solution 💡 https://codesandbox.io/s/state-gallery-solution-l6wmmf
Use State in the Card Component
Use one useState Hook to save for each Card if it is in the Cart and if it is a Favorite. Bonus: save which Size is selected.
Use that State to toggle the buttons i.e. show "✓ Added to Cart" and change the color of the Favorite Button.
https://codesandbox.io/s/galaxus-mini-events-solution-7736rz
Solution 💡 https://codesandbox.io/p/sandbox/galaxus-mini-state-solution-jpljqp
Overall, using an updater function with setState provides flexibility, ensures the most up-to-date state, and allows for complex state updates or performance optimizations.
When the next State depends on the previous State it's best to use an updater function.
Counter Example https://codesandbox.io/s/counter-updater-function-dpkxdh
More Info: https://react.dev/reference/react/useState#setstate-caveats
State can hold any kind of JavaScript value, including objects. But you should not change objects that you hold in the React state directly.
Instead, when you want to update an object, you need to create a new one (or make a copy of an existing one), and then set the state to use that copy.
Example Mutating the State directly does not work
https://codesandbox.io/s/state-mutating-objects-j3hs62
We need to use the setState Function to Update the state. And we need to copy the existing data into it.
Object Spread Syntax:
To update a nested Object, we need to copy each "Level".
Changing the city
prop:
Replace the Updating Logic
Use Immer JS to simplify the state Updating Logic.
https://codesandbox.io/s/state-immer-kkcz78
Solution 💡 https://codesandbox.io/s/state-immer-solution-2m3zw7
Add something:
Change something:
More Examples: https://react.dev/learn/updating-arrays-in-state
Sometimes, you want the state of two components to always change together.
To do it, remove state from both of them, move it to their closest common parent, and then pass it down to them via props.
Notice how pressing one panel’s button does not affect the other panel—they are independent.
But now let’s say you want to change it so that only one panel is expanded at any given time.
Step 1: Remove state from the child components
Step 2: Add state to the common parent
Lift State Up
Lift the state handleAddToCart up from the Card Component into the App Component.
Bonus: show on the Cart Icon how many Products are currently in the Bag.
https://codesandbox.io/p/sandbox/galaxus-mini-state-solution-jj4jg3
Solution 💡 https://codesandbox.io/p/sandbox/galaxus-mini-state-up-solution-53vrq9
Context lets the parent component make some information available to any component in the tree below it—no matter how deep—without passing it explicitly through props.
The only argument to createContext
is the default value.
Import the useContext
Hook from React and your context.
Read the value from the context you just imported.
Even though we’re using the context, we have not provided it yet. React just uses the default value we specified in the previous step.
Replace prop drilling with context
Toggling the checkbox changes the imageSize
prop passed to each <PlaceImage>
. The checkbox state is held in the top-level App
component, but each <PlaceImage>
needs to be aware of it.
Currently, App
passes imageSize
to List
, which passes it to each Place
, which passes it to the PlaceImage
. Remove the imageSize
prop, and instead pass it from the App
component directly to PlaceImage
. You can declare context in Context.js
.
https://codesandbox.io/s/context-prop-drilling-2fl3yn
Solution 💡 https://codesandbox.io/s/context-prop-drilling-solution-x62vmf
Context is very tempting to use! However, this also means it’s too easy to overuse it. Just because you need to pass some props several levels deep doesn’t mean you should put that information into context.
Good Use Cases for context
Use Context for the Bag Count
Don't pass the count
via Props to the MiniCart Component. Instead, use Context to provide it.
Bonus: Use Context to Provide a Dark Theme to every Component. The Theme is currently hardcoded to light
in most Components.
https://codesandbox.io/p/sandbox/galaxus-mini-state-up-solution-dng35k
Solution 💡 https://codesandbox.io/s/galaxus-mini-context-solution-0tq066
The Effect Hook lets you perform side effects in function components.
A side effect can be data fetching, setting up a subscription or manually changing the DOM.
In other words: Effects let you run some code after rendering so that you can synchronize your component with some system outside of React.
Resize Event Listener
https://codesandbox.io/s/use-effect-example-2n8ct9
Per default, the side effect inside useEffect runs on every Render.
We can provide a Dependency Array to run the effect only when one of the dependencies changes.
1. Side Effect Runs After Every Render
3. Side Effect Runs After State Value Changes
2. Side Effect Runs Only Once After Initial Render
4. Side Effect Runs After Props Value Change
Certain effects need cleanup when the React Component unmounts.
Whatever we return from an useEffect Hook will be run when the Component unmounts.
Some Examples
In simple application we can use the useEffect Hook for Data Fetching.
Often times, a custom hook is created with useEffect for Data Loading.
1. You don’t need Effects to transform data for rendering. Just do it in the render method.
2. You don’t need Effects to handle Events.
For example, let’s say you want to send an /api/buy
POST request and show a notification when the user buys a product. Just do it in the Event Handler.
Fetch the Products and the Cart
Fetch data by using two useEffect Hooks. Use getProducts()
and getCartItems()
to load data from the API.
https://codesandbox.io/s/galaxus-mini-use-effect-d2w0bw
→ neue Sandbox mit Loading Spinner, bitte diese verwenden.
Solution 💡 https://codesandbox.io/s/galaxus-mini-use-effect-solution-br9yxq
We use React Router for our application.
A relative <Link to>
value (that does not begin with /) resolves relative to the parent route. It may contain .. to link to routes further up the hierarchy.
Dashboard will render in the <Outlet>
:
<DashboardMessages>
when the URL is "/messages"<DashboardTasks>
when the URL is "/tasks"null
when the URL is"/"Route to the Cart Page
Use React Router to create a new Cart Page and navigate to it.
https://codesandbox.io/p/sandbox/galaxus-mini-router-4v5kqc
→ neue Sandbox mit Router, bitte diese verwenden.
Solution 💡 https://codesandbox.io/s/galaxus-mini-router-solution-cp5w7d
In this chapter we will cover some common Use Cases for TypeScript with React.
A comprehensive overview can be found in the following Cheatsheet:
Props and State
You can define your Props using a Type or using an Interface. Using a Type seems to be a more common these days for for typing React Props and State.
Extending Props
Sometimes you want to take component props declared for one component and extend them to use them on another component. But you might want to modify one or two.
Some Examples
Here are just some common examples for typing Props.
React uses its own event system. That’s why you can’t use typical MouseEvent or similar on your elements. You need to use the specific React version
One of the most common cases is correctly typing the onChange
event used on an input field in a form.
TypeScript can infer the values of the useState
hook:
When you need to initialize a hook with a null-ish value, you can make use of a generic and pass a union to correctly type your hook:
You don’t need to provide any extra typings. TypeScript will check that the method signature of the function you provide is correct — and makes sure you return a function for the cleanup.
You can type the Context by using a Generic in the createContext
Function.
Add Typings to the Application
Add Typings for cartProducts
State in the App
Component.
Add Typings for Card
Props
and CardState
in the Card
Component.
https://codesandbox.io/s/galaxus-mini-typescript-ud8775
→ neue Sandbox mit TSX, bitte diese verwenden.
Solution 💡 https://codesandbox.io/s/galaxus-mini-typescript-solution-37fsmu
Global State Management helps with Prop Drilling, accessing Data without duplicating it, Data Fetching...
Global State Management can be divided in Client State and Server State.
Until now we just used setState
and Context
for Client State. And we used useEffect
for Server State.
Client State
Server State
This kind of state lives only on the Client and is not persited on the Server. It is synchronous, not async.
Use Cases:
Out of the Box
For simple State that lives inside of a component we can use setState
. We can lift state up and provide it to some Child Components. With Context
we can provide it to all components.
Most applications do not have complex Client state. With Context
(and possibly useReducer
) we can build our own a simple State Management.
useReducer
tbd
Libraries
Redux is the de-facto standard. It can also handle Server State and has awesome DevTools. People complain about too much boilerplate to accomplish simple things. Redux Toolkit solves a lot of those issues.
MobX provides simple, scalable state management by using Observables.
Zustand is a newer Library that is currently hyped.
Redux
tbd
Managing Server State is a lot harder than Client State.
Things we need: Fetching, Caching, Invalidating and Updating Server Data. And Loading States.
Bonus Features: Polling, Optimistic Updates, Pagination, Prefetching server data.
Out of the Box
In a useEffect
Hook we can fetch data from a Server and use useState
to save and re-render the Component.
...but it has a few downsides and lacks a lot of useful features.
Why not just useEffect?
Fetching directly in Effects makes it easy to create “network waterfalls”. You render the parent component, it fetches some data, renders the child components, and then they start fetching their data.
Fetching directly in Effects usually means you don’t preload or cache data. For example, if the component unmounts and then mounts again, it would have to fetch the data again.
Fetching all data of your application in the <App>
component loads unnecessary data and does not scale well.
Libraries
React Query and SWR a popular Libraries for managing Server State.
When using GraphQL, Apollo is a popular and comprehensive Library.
Many Libraries tried to solve Client and Server State in one Library i.e. Redux Toolkit Query.
Modern State Management Libraries focus on solving just one of those, because the challenges are completely different.
Let's have a look at a modern Server State Library, React Query.
React Query in 100 Seconds
https://www.youtube.com/watch?v=novnyCaa7To
Example
https://codesandbox.io/s/react-query-intro-dq5jcj
Queries
A query is a declarative dependency on an asynchronous source of data that is tied to a unique key.
The unique key you provide is used internally for refetching, caching, and sharing your queries throughout your application.
Queries
The query result contains all of the information about the query that is needed for templating and any other usage of the data.
Stale Queries
Per default, every cache Query is considered as stale data.
Stale queries are refetched automatically in the background when:
Named Queries aka Custom Hooks
Each query can be refactored to be it's own custom hook. This design is the suggested way to use React Query, as it makes it much easier to manage query keys and shared query logic.
Invalidating Queries
Waiting for queries to become stale before they are fetched again doesn't always work, especially when you know for a fact that a query's data is out of date because of something the user has done.
Mutations
Mutations are used to create/update/delete data. For this purpose, React Query exports a useMutation
hook.
Optimistic Updates
Use React Query for Server State
useQuery
to use a custom Query useCartProducts.ts
https://codesandbox.io/s/galaxus-mini-state-management-uykx2s
Solution 💡 https://codesandbox.io/s/galaxus-mini-state-management-solution-ttp2id
Test Runner & Tools
jsdom
. Alternatives: Mocha, ava.Snapshot Testing
Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly.
Unit Testing
The React Testing Library is a light-weight solution for testing React components. It provides utility functions in a way that encourages better testing practices.
The goal is not to test implementation details but real user interactions.
Testing Library runs on top of Jest and is a replacement for Enzyme.
Finding DOM Nodes
The utilities facilitate querying the DOM in the same way the user would i.e. finding elements by their label text, finding links from their text etc. like a user would.
HTML
Testing Library Query
Finding DOM Nodes
It also exposes a recommended way to find elements by a data-testid
as an "escape hatch" for elements where the text content and label do not make sense or is not practical.
HTML
Testing Library Query
Debugging
For convenience screen
also exposes a debug
method in addition to the queries. It supports debugging the document, a single element, or an array of elements.
User Actions
Examples
https://codesandbox.io/s/testing-library-examples-gk2htq
Siehe Files im src/__tests__
folder.
Write some Tests
Download the Sandbox as ZIP and add a Snapshot Test.
Write a new Test to check if a Product was marked as Favorite.
https://codesandbox.io/s/galaxus-mini-testing-library-46ey1n
Solution 💡 https://codesandbox.io/s/galaxus-mini-testing-library-solution-ji8ckv
Controlled vs. uncontrolled Forms
HTML form elements work a bit differently from other DOM elements in React, because form elements naturally keep some internal state.
There are two approaches: controlled and uncontrolled Forms.
https://codesandbox.io/s/forms-uncontrolled-controlled-1l0nfe
Controlled vs. uncontrolled Forms
With Controlled forms, we have access to all values all the time. This can be useful for i.e. validation checks.
The recommendation is to use controlled Forms.
Library
Formik is a great Library for Forms.