Coder Social home page Coder Social logo

Comments (5)

jmyrland avatar jmyrland commented on May 28, 2024 1

What I was trying to achieve, however, is to have a separate slice of state per entity (department in my example) and use all the good things like actions, thunks, computed props etc. I do appreciate it might not be exactly possible though.

It might be possible, but I currently do not know a way to achieve this in the current version of easy-peasy.

from easy-peasy.

jmyrland avatar jmyrland commented on May 28, 2024

hey @yard !

A dummy example: I want to have Company store which would contain multiple Department slices. Company store would operate departments on a higher level (add, remove, rename etc), whereas Department slice would hold actions for manipulating a single Department (e.g. manage a collection of users within). The key requirement is to keep the state separate for each department yet have them organized under a single parent store for higher-level manipulations.

I'm guessing in this case, that you would like a dynamic set of companies, each with their respective departments. And that you would prefer an "object oriented" approach, e.g. company.addDepartment(...).

As far as I know, this feature does not currently exist. (Correct me if I'm wrong, @no-stack-dub-sack / @ctrlplusb)

This is how I would solve this particular scenario:

import { action, Action } from "easy-peasy";

interface ICompany {
  id: string;
  name: string;
  departments: IDepartment[];
}

interface IDepartment {
  name: string;
  employees: IEmployee[];
}

interface IEmployee {
  name: string;
}

export interface StoreModel {
  companies: ICompany[];

  addOrUpdateCompany: Action<this, ICompany>;
}

const storeModel: StoreModel = {
  companies: [
    {
      id: "umbrella-corp",
      name: "Umbrella",
      departments: [
        {
          name: "International Investigation Department",
          employees: [{ name: "P.T." }, { name: "O'Neal" }]
        },
        {
          name: "North American division",
          employees: [{ name: "Lauper, Douglas" }]
        }
      ]
    }
  ],

  addOrUpdateCompany: action((state, newComp) => {
    const existingCompIndex = state.companies.findIndex(
      (c) => c.id === newComp.id
    );

    if (existingCompIndex !== -1) {
      state.companies[existingCompIndex] = newComp;
    } else {
      state.companies = [...state.companies, newComp];
    }
  })
};

export default storeModel;

This does not solve your issue, but it exposes the opportunity to create a functional wrapper for each company via a custom hook - utilizing the addOrUpdateCompany action, e.g:

const useCompany = (company: ICompany) => {
  const addOrUpdateCompany = useStoreActions(
    (store) => store.addOrUpdateCompany
  );

  return {
    // Keep state
    ...company,
    // Extend with actions
    addDepartment: (newDepartment: IDepartment) => {
      addOrUpdateCompany({
        ...company,
        departments: [...company.departments, newDepartment]
      });
    },
    // Extend departments state with actions
    departments: company.departments.map((department, dIndex) => ({
      // Keep state
      ...department,
      // Extend with actions
      addEmployee: (newEmployee: IEmployee) =>
        addOrUpdateCompany({
          ...company,
          departments: company.departments.map((d, index) =>
            dIndex === index
              ? {
                  ...department,
                  employees: [...department.employees, newEmployee]
                }
              : d
          )
        })
    }))
  };
};

This is an abstraction on top of a single company - allowing you to add departments, as well as adding employees to a specific department.

const company = useCompany(companyState);

company.addDepartment({...});

company.departments[0].addEmployee({...});

I've created a sandbox to verify this implementation, if you would like to take a deeper look.

from easy-peasy.

yard avatar yard commented on May 28, 2024

Hi @jmyrland!

Thank you for a throughout example. Yep your approach is legit, but it essentially provides shortcuts to otherwise global actions operating on the whole state. Another take would be to avoid hooks and just capture references to store's actions and use them to craft "methods" on the nested entities:

const storeModel = {
  departments: [],

  _addOrUpdateDepartment: action((state, [department, actions]) => {
    const index = state.departments.indexOf(department);

    if (index === -1) {
      state.departments.push(department);
    } else {
      state.departments[index] = department;
    }

    department.save = () => actions._addOrUpdateDepartment(department);
    department.delete = () => actions._removeDepartment(department);
  }),

  _removeDepartment: actions(() => {
    const index = state.departments.indexOf(department);
    state.departments.splice(index, 1);
  }),

  addDepartment: thunk((actions, department) => {
    actions._addOrUpdateDepartment([department, actions]);
  })
};

What I was trying to achieve, however, is to have a separate slice of state per entity (department in my example) and use all the good things like actions, thunks, computed props etc. I do appreciate it might not be exactly possible though.

from easy-peasy.

no-stack-dub-sack avatar no-stack-dub-sack commented on May 28, 2024

@yard So-if I understand you correctly, ideally you'd want something like this?

interface DepartmentModel {
  id: string;
  name: string;
  employees: IEmployee[];
  addEmployee: Action<this, IEmployee>;
}

interface StoreModel {
  addDepartment: Thunk<this, IDepartment | IDepartment[]>;
  removeDepartment: Thunk<this, string | string[]>;
  departments: DepartmentModel[]; // entries are dynamically added slices
}

from easy-peasy.

no-stack-dub-sack avatar no-stack-dub-sack commented on May 28, 2024

@yard Closing this for the time being pending any further questions. Feel free to reopen if you'd like to continue the discussion. Thanks!

from easy-peasy.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.