redux toolkit upsert one
Have you ever heard of the term "upsert"? It means "create, or update if one is already created". That's right, two operations, one function. And redux toolkit has a way to do that.
In our toy app over in the browser panel, you can play around with this. Create a new mission, and see it appear in the rows. Edit an existing mission, and see it get updated. Amazing!
So how does this magic work? Well, if I can direct your attention to missionsSlice.js
, you'll see that we are now adding an "upsert" key in our reducers
object in the createSlice
call. Inside here, we're going to use our missionsAdapter
's upsertOne
function. We're declaring an inline reducer here, reducers always take the arguments state, action
which refer to the global state, and the action object. Actions have a payload
key, which is the data we want to upsert. So the upsertOne
function requires us to pass in the state
and the data, and that's all there is to it.
Using this reducer requires us to dispatch
an action. How do we get this action? Well, we can find the actions from the missionsSlice.actions
object, declared at the bottom of missionsSlice.js
. Here, we destructure off upsert
and call it upsertMission
so we can use it in our app.
To use the action, we need a special hook called useDispatch
from react-redux
. You can think of this as "writing data" and the useSelector
as "reading data".
Updating our data is as easy as calling dispatch(upsertMission( [new mission object]))
. We're upserting the mission with the current mission object, but with the new title and description merged onto it.
NewMission.js
also dispatches an upsertMission
action, but with a random id, and new attributes as we're creating a new mission object here.
That's it. Now, we can be confident that our updates are flowing nicely throughout our app- once we update a mission, we can go view it on the Missions route, as our selector will automatically pick up on the update.
Next, we'll do a similar pattern but for deleting data.
import { createSlice, createEntityAdapter } from "@reduxjs/toolkit"; import defaultMissions from "./missionData"; const missionsAdapter = createEntityAdapter(); const emptyInitialState = missionsAdapter.getInitialState(); const initialState = missionsAdapter.upsertMany( emptyInitialState, defaultMissions ); const missionsSlice = createSlice({ name: "missions", initialState, reducers: { upsert: (state, action) => missionsAdapter.upsertOne(state, action.payload), }, }); export const { selectAll: selectAllMissions, selectById: selectMissionById } = missionsAdapter.getSelectors((state) => state.missions); export const { upsert: upsertMission } = missionsSlice.actions; export default missionsSlice.reducer;