Building the Event Sign Up App
Learn how to build our very own Event Sign-Up App using the React Redux library with a centralized state management store.
Building the Event SignUp app#
For the remaining three lessons in this module we’ll be building three approaches to using Redux. In this first lesson we’re going to build arguably the most complex of the three approaches. We’ll be using the React Redux library to help us, before transitioning to the Redux Toolkit for the next lesson.
Finally, we’ll see how to implement the redux pattern without any additional libraries using React’s built-in
useReducer Hook and the Context system.
First, though, let’s get going with building the Event SignUp app and the vanilla React Redux package.
We’re using Create React App for the remaining lessons and the setup process should be starting to feel familiar by now.
Open up a terminal window to the parent folder that you want to create the new project in and run the following command:
Once the command finishes and our app base is installed, run it to test it’s set up correctly with:
View the running starter app in your browser at
Cleaning up the starter project#
Just like in previous lessons, let’s clean up the starter app by doing the following:
index.jsand remove the reference to
Open the main
App.jsfile and empty the file so we can add our demo-specific code later on.
Adding project dependencies#
Before we create the files we need for our demo, let’s bring in the required dependencies.
Bringing Redux, React Redux, and Redux Thunk onboard#
The first dependencies to add are Redux, React Redux and Redux Thunk (we’ll explain this last one when we come to configure our redux store). Back in a terminal window, make sure you’re in the root project location and enter the following command:
With that done, we can access the various methods and helpers that React Redux provides us.
Since we’ll be adding event attendees to our fake event, it’s good practice to give each of them a unique ID value. We could just increment the ID to the next highest in the array of event attendees, but to make it more realistic we’ll assign a uniquely generated ID instead.
To do that, let’s import the very helpful React UUID package with the following command:
We’ll stick with the Node version of Bulma for this demo, adding it to our project using this command:
Finally, we’ll also add in Node Sass to compile our
.scss files for us. Bring in the final package as a dev-only dependency as follows:
Creating our app’s files#
With all our dependencies added, let’s create all the files we need for the project and build them out.
index.js- we’ll need to make a scant few changes in here to wire up our redux store with the rest of the application.
App.js- the main starting point of our app.
/assets/style.scss- we’ll add in some basic styling niceties in the scss version of Sass.
/components/EventDetails.jsx- a presentational component that will display details of an individual event.
/components/EventSignUpForm.jsx- a Bulma-styled form component whose logic will enable us to add new attendees to the global store via redux methods.
/components/EventSignUpList.jsx- the other side of the event form coin, this component will allow administrators to edit attendees, remove them, and see their details at a glance in a list format.
/components/Header.jsx- another presentational component with scant logic that will act as a navbar and display a message to the user if they’re logged in.
/data/initialState.js- a starting point for the global state in our app.
/reducers/eventReducer.js- this will hold all the reducer functions, state-altering magic and actions for dealing with the event portion of our app’s state.
/reducers/reducers.js- this will be the main reducer file that we associate with our store and app’s redux - effectively it acts as a container for the other, more specific reducers, such as the event reducer.
With the files created, let’s work through them to add in the details needed.
/assets/style.scss and copy in the following styles:
import statement at the top of the file brings the Bulma CSS framework styles into the project. Next, we set a couple of SCSS variables and a couple of styles to bring a little nicety to our base app.
state represents a central data storage facility that we can use to house any relevant data we need to share and use across our app. When we create our redux store, we’ll supply it with an initial dataset that represents a starting point; the initial state of our
state as it were.
/data/initialState.js file is going to house such a starting point, so open it up and enter in the following:
events property on the object that will hold any state data relevant to the events section of the app. You can define your app’s
state however you like, but I prefer to have a single initial state file and split it into slices like this.
Now let’s tackle the
reducers.js file. A redux store can only deal with a single reducer. This presents a problem when we have a larger app working on affecting different slices of the app’s
state with very different reducer statements and actions and other moving redux parts.
If we were to have only a single reducer, you can imagine that this would grow huge over time and become a maintenance nightmare for us developers. A much better approach would be to have multiple reducers that only deal with a distinct area of the app, however we would then fall foul of the ‘single reducer to a store’ rule…
Fortunately for us the React Redux library provides us with a handy function,
combineReducers , which allows us to define our reducer files separately and combine them into a single master reducer (of sorts) that we can then pass into the redux store.
That’s what we’ll be doing with the
reducers.js file. First, we’ll import the
combineReducers function from the ‘redux` package:
And then import the
eventReducer from the
/reducers/eventReducer.js file. In a bigger app you might have more reducers, but we’ve only got the one here.
Finally, we’ll export the result of the
Now we come to the main event, even though we’re only a couple of files into the demo and have a few more to complete. However, logically, it makes sense to work through the
eventReducer file now and cement the key concepts about Redux in place before creating and wiring up the other parts of the redux puzzle.
Open up the
/reducers/eventReducer.js file and let’s begin with the imports:
We’re pulling in the
uuid library and our
initialState file here. The
uuid package will be used to generate a unique ID value when we come to add a new event attendee into state via the upcoming reducer function.
Action types are simple string constants that describe the type of action that we want to perform against state. You do not have to do this, or use action types at all, they are just a well-adopted convention. If you choose not to use them then you’ll have to pass around string values here and there. This introduces lots of potential for mistakes such as spelling errors, and that’s without considering that you might want to change any of those strings — in this case, we only have to change them in one place.
Let’s define the action types for the
Actions are different from action types which are simple describing strings. Actions are functions that return both the type of change that we want to perform against our app’s
state (such as adding a new user, deleting a row or fetching a result), and a payload, an object containing the changes to
For example, if we wanted to add a new user, we’d create an action that had a type of
add-new-user and a payload that was a new
Again, you don’t have to actually do this. However, ultimately we’re going to call a
dispatch function (provided to us by the Redux library) which dispatches a notification to the reducer to make some changes to our app’s
dispatch function requires a type of action (as we’ve already created) and a payload.
Without creating any action functions, we’d have to call each and every
dispatch like this:
As it stands this might not look like much, but imagine that you have a bunch of these updates in the same component and across multiple areas of the app. It’ll start to become cumbersome and bloated to keep doing this as time goes on. Plus, we’re back to the potential for errors and making it harder to make changes; if we wanted to change the payload here, we’d have to find every single instance of this and change it across multiple files.
Compare this with having a single function call that is passed any data changes as parameters:
It’s much easier and cleaner to define and export a series of action functions in each relevant reducer file that accepts one of more state data changes as parameters, and that return a nicely formatted action object.
That’s what we’ll do now. We’re going to create three action functions here:
addEventAttendeefor adding new event attendees
toggleEventAttendancefor toggling whether or not someone is coming to the event
deleteEventAttendeeto remove anyone who is no longer needed from our event signup list
We’re exporting each function so we can call it across our app in whichever component needs to use it. You can see that they’re all pretty straightforward arrow functions. There’s nothing specific to Redux here, but we’ve formatted the return object with a
type property (using one of the action types we defined earlier) and a
payload which contains any relevant data that needs to be used by the upcoming reducer to affect our
The reducer is where all roads point to, and is responsible for making the changes and updates in state.
Remember, it’s important to understand that app
statemust be immutable, that is, we do not change it. When we talk about changes or updates it’s mainly for conversational convenience. In reality, the whole redux setup is centered around making a new copy of state with changes applied to it.
It’s going to look a little unwieldy at first, but stick with it and you’ll soon make sense of it as the pattern will become familiar. We’ll also be looking at the Redux Toolkit in the next lesson which reduces a lot of this initial complexity in writing reducers.
Let’s define the reducer function with just a single action handler in it:
Again, the reducer is a standard arrow function that accepts a value for state and an action. These values will be passed to the reducer by the React Redux library when everything is wired up so the only thing we have to do at this stage is supply an initial value for state. In our case, this will be the
events portion of the
initialState object we imported earlier.
The main body of the reducer will be a
switch statement that checks the incoming
type property for a match. When we find a match (in this case when
actions.ADD_EVENT_ATTENDEE) we can perform some updates to state.
First, we pull out the
content values from the action’s
payload property via destructuring. Then, we need to return a new, updated copy of
state with our changes applied.
Notice that we’re including the copy of existing
state using the
... spread syntax. Also keep in mind that this reducer will be acting upon the
events portion of the overall state. This is wired up via the Redux library, but it stems from our inclusion of the
state = initialState.events when we defined this reducer function.
We want to update the
eventAttendees property (an array) so we use the spread syntax again and add the new event attendee info from the
content object. We’ll add the
id value too and set a new property,
true because when someone signs up for the first time we assume they’re attending.
Another point of note is that you should always return
statefrom a reducer, even if it’s unaltered. Notice at the end of the
switchstatement we’re just returning the plain
stateobject via the
defaultswitch catch-all case.
Next, we’ll sort out the code to handle the
OK, so things are starting to look a little messy and that’s a fair criticism that’s levelled at redux from time to time. However, you can start to see a similar pattern between these different action matches. For the
We pull out the
idvalue and use it to search for a matching event attendee during an
When we find a match, we update the
attendingproperty to its reverse.
Finally, we return a new
statecopy once more with our
updatedEventAttendeesvariable assigned to the
DELETE_EVENT_ATTENDEE we’re performing a very similar process to the one we've just done, only this time the
updatedEventAttendees variable will be populated via
Array.filter(), removing the event attendee whose
id value matches the one supplied to the reducer.
Well done! You’ll be pleased to know that we’re done with the reducer file and it’s all downhill from here, as we build out the remaining files.
Completed reducer file#
With all the code in, the finished and complete
eventReducer.js file will look like this:
Open up the
/config/configureStore.js file and fill out the following code:
What we’re doing in this file is stitching together our root reducer, initial state values, and what we call enhancers. Enhancers are higher-order functions that enhance a redux store, adding in middleware, persistence capabilities or other additional capabilities that we may want our store to have.
In our case we’re adding a very common middleware, Redux Thunk, which we pulled into our project in the dependencies section. The Thunk middleware allows for asynchronous actions during redux operations. Granted, it’s not something we’re doing here in this demo, but you’ll use this frequently, especially when dealing with API calls or other asynchronous mechanisms in larger projects out in the real world.
So, we pull in our imports, the root reducer and initial state.
Next, we define an arrow function that creates a middleware enhancer by calling the
applyMiddlewarefunction and passing it the
composedEnhancersis a little redundant here as we only have the one, but we call the
composefunction from redux and pass it the result of the
All that remains is to call the
createStorefunction and pass it our reducer, starting state and the
composedEnhancersvariable, returning the
storevalue to the caller.
It might look a little alien at this point, but for now, just be aware that adding middleware to your redux stores is quite common in a lot of projects and the process is usually the same:
Define a store-configuring function
Within it, apply any middleware you need
Compose an enhancer from the applied middleware
Create a redux store that wires up your reducers, initial state and enhancers
Return the store for use
Header component will be a straightforward website header with some navigation elements in it. Open up the
/components/Header.jsx file and let’s start with the classic React import line.
After this, let’s define the entire presentational component like this:
We’re destructuring three properties from
isSignedIn will be a boolean value to determine if we’re logged in to the admin side of things.
handleSignOutClick will be used to handle the button clicks for signing in and out respectively.