How to migrate app state from React Context to Redux Tool Kit?

Viewed 553

What I'm trying to achieve:

I have a NextJS + Shopify storefront API application. Initially I set up a Context api for the state management but it's not that efficient because it re-renders everything what's wrapped in it. Thus, I'm moving all state to the Redux Toolkit.

Redux logic is pretty complex and I don't know all the pitfalls yet. But so far I encounter couple problems. For example in my old Context API structure I have couple functions that take a couple arguments:

    const removeFromCheckout = async (checkoutId, lineItemIdsToRemove) => {
        client.checkout.removeLineItems(checkoutId, lineItemIdsToRemove).then((checkout) => {
            setCheckout(checkout);
            localStorage.setItem('checkout', checkoutId);
        });
    }

    const updateLineItem = async (item, quantity) => {
        const checkoutId = checkout.id;
        const lineItemsToUpdate = [
            {id: item.id, quantity: parseInt(quantity, 10)}
        ];
        client.checkout.updateLineItems(checkoutId, lineItemsToUpdate).then((checkout) => {
            setCheckout(checkout);
        });
    }

One argument (checkoutId) from the state and another one (lineItemIdsToRemove) extracted through the map() method. Inside actual component in JSX it looks and evokes like this:

<motion.button 
    className="underline cursor-pointer font-extralight"
    onClick={() => {removeFromCheckout(checkout.id, item.id)}}
>

How can I declare this type of functions inside createSlice({ }) ? Because the only type of arguments reducers inside createSlice can take are (state, action). And also is it possible to have several useSelector() calls inside one file?

I have two different 'Slice' files imported to the component:

    const {toggle} = useSelector((state) => state.toggle);
    const {checkout} = useSelector((state) => state.checkout);

and only the {checkout} gives me this error: TypeError: Cannot destructure property 'checkout' of 'Object(...)(...)' as it is undefined.

Thank you for you're attention, hope someone can shad the light on this one.

1 Answers

You can use the prepare notation for that:

const todosSlice = createSlice({
  name: 'todos',
  initialState: [] as Item[],
  reducers: {
    addTodo: {
      reducer: (state, action: PayloadAction<Item>) => {
        state.push(action.payload)
      },
      prepare: (id: number, text: string) => {
        return { payload: { id, text } }
      },
    },
  },
})

dispatch(todosSlice.actions.addTodo(5, "test"))

But 99% of the cases you would probably stay with the one-parameter notation and just pass an object as payload, like

dispatch(todosSlice.actions.addTodo({ id: 5, text: "test"}))

as that just works out of the box without the prepare notation and makes your code more readable anyways.

Related