Intro to Redux Toolkit
In this module we'll take an app that has a bit of local state and turn it into a global state machine with the glorious power of Redux Toolkit.
Why use Redux?
One of most commonly asked questions is whether someone needs Redux in their app or not. Through the lessons in this module, we'll show you why having a global state manager like Redux is necessary.
Simply put: Components need to share data with one another. Prior to Redux, we would put our app state at the top level of our component tree, and pass the global state all the way down into the lowest component. This is commonly referred to as "prop drilling". It was very tedious to manage. Also, if a component needed to access global state that wasn't passed in through a prop, it wasn't possible.
Redux allows you to read and update global state safely through the concept of a global store. The store is just a big javascript object. However, you can't edit it like normal javascript objects. Instead, you "dispatch actions".
This is the beginning of where people get confused. Why do we need a library to update a simple javascript object? As I discussed earlier, technically you don't. You can make a big object at the top of your app and share it with components. This would work fine.. until your app gets big.
Big production apps are what really benefit from Redux. Data objects from dozens of entities with hundreds of attributes shouldn't be casually updated on the client. Imagine if a component could just change attributes on this giant object, and you were trying to debug what was going on. You wouldn't have any papertrail, and you'd have to trace down every component that could update that object.
Why dispatch actions?
Redux introduces dispatching actions, which you can think of as a consistent way (perhaps an intentionally over-engineered approach) to notifying our store of events that should change data. All events that change data should go through Redux Actions. So, debugging changes in your data is as easy as looking the list of dispatched actions. Saying "dispatching actions" forces you to use fancy terminology to talk about something quite simple.
Actions are received by reducers
An action can be listened for by a reducer, which is just a simple javascript function that returns the new state after the data update is applied. It makes everything nice and consistent. We know that all updates must go through actions, and all data changes must go through reducers. This is immensely helpful when debugging! In fact, one of Redux's first main selling point was "time travel", where you can easily reverse actions to previous states. Without an abstraction to represent data mutations, we couldn't easily backtrack our changes.
Redux toolkit setup
In the example to the right, you'll see we have setup redux by importing the Provider
from react-redux
. react-redux
is the connector library between React and Redux. We wrap our app in a Provider, and pass in a store. This is all you need to setup your app with redux at the App level.
Check out store.js
for the Redux toolkit store setup. We call configureStore
with an object that we'll configure to set up our reducer. We have one reducer setup, the missions
"slice", and pass in a missionsReducer
.
The missionsSlice.js
calls createSlice
, which sets up the missions slice, you can think of this as a named entity. We have missions
as the name, initialState
as an empty object for now, and no reducers in the object. We'll export missionsSlice.reducer
and pass it to our store.
This setup doesn't do anything just yet, and you might be confused as to what all this does. Sit tight- we'll continue building this out in the next few lessons.
import styled from "styled-components"; import { BrowserRouter, Routes, Route, Outlet, Link } from "react-router-dom"; import NavBar from "./Components/NavBar"; import Home from "./Home"; import Missions from "./Missions/Missions"; import NewMission from "./Missions/NewMission"; import Mission from "./Missions/Mission"; // Import the Provider from the react-redux library import { Provider } from "react-redux"; // Import our store! import { store } from "./Store/store"; const OutletContainer = styled.div` margin: auto; max-width: 800px; padding: 20px; `; function Layout() { return ( <> <NavBar /> <OutletContainer> <Outlet /> </OutletContainer> </> ); } function Logs() { return <div> Logs </div>; } function Account() { return <div> Account </div>; } function NotFound() { return <div> Not found </div>; } function App() { // Render the Provider around our app with the store return ( <Provider store={store}> <BrowserRouter> <Routes> <Route element={<Layout />}> <Route index element={<Home />} /> <Route path="missions" element={<Missions />} /> <Route path="missions/new" element={<NewMission />} /> <Route path="/missions/:id" element={<Mission />} /> <Route path="logs" element={<Logs />} /> <Route path="account" element={<Account />} /> <Route path="*" element={<NotFound />} /> </Route> </Routes> </BrowserRouter> </Provider> ); } export default App;