2

I'd like to break down my zustand state into smaller reusable objects; but, the documented zustand patterns all seem to involve promoting operations up to the top-level store object. Is there a way to allow mutations on the inner objects, without necessarily repeating every operation for every object?

By way of example, let's say we have a vehicle object:

function makeVehicle() {
  return { going: false };
}
function startVehicle(vehicle) {
  return { ...vehicle, going: true };
}
function stopVehicle(vehicle) {
  return { ...vehicle, going: false };
}

I can have a state that contains two named vehicles

import { create } from 'zustand';
export default create(set => {
  blueVehicle: makeVehicle(),
  greenVehicle: makeVehicle()
});

But, it seems like if I want to also export the mutators, I need to repeat each operation, for each object, with a fair bit of boilerplate:

import { create } from 'zustand';
export default create(set => {
  blueVehicle: makeVehicle(),
  startBlueVehicle: () => set(
    state => ({ blueVehicle: startVehicle(state.blueVehicle) })
  ),
  stopBlueVehicle: () => set(
    state => ({ blueVehicle: stopVehicle(state.blueVehicle) })
  ),
  greenVehicle: makeVehicle(),
  startGreenVehicle: () => set(
    state => ({ greenVehicle: startVehicle(state.greenVehicle) })
  ),
  stopGreenVehicle: () => set(
    state => ({ greenVehicle: stopVehicle(state.greenVehicle) })
  )
});

Is there a simpler way to express this?

David Maze
  • 130,717
  • 29
  • 175
  • 215

1 Answers1

2

For this particular example, I'd structure the original state as follows:

import { create } from 'zustand';

export default create(set => {
  blueVehicle: makeVehicle(),
  greenVehicle: makeVehicle(),
  startVehicleByName: (vehicleName) => set(
    state => ({ [vehicleName]: startVehicle(state[vehicleName]) })
  ),
  stopVehicleByName: (vehicleName) => set(
    state => ({ [vehicleName]: stopVehicle(state[vehicleName]) })
  )
});

This way, if you want to start the blue vehicle, you can call startVehicleByName('blueVehicle')

Or if you want to stop the green vehicle, you can call stopVehicleByName('greenVehicle')

As pointed out in the zustand readme, you can put anything in the store. So, there's nothing wrong with putting functions with (potentially any number of) parameters into it.

One of the biggest selling points of zustand (at least for me) is how much freedom it allows. It's unopinionated, so you can implement robust flux patterns there if you need them, or back down from it and have it less boilerplate-y for simpler apps (or portions of apps).

YSK
  • 528
  • 4
  • 7