Comments (5)
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.
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.
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.
@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.
@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)
- Misleading documentation on destructuring compted properties HOT 9
- Differences between `computed` and a hook HOT 6
- Non-stylistic differences between actions and thunks? HOT 2
- undefined store on "contextmenu" event HOT 2
- Cannot assign other store action to property HOT 4
- RangeError "Maximum call stack size exceeded" on clone function HOT 3
- Unable to alias `Unstable_EffectOn` type
- Inform users about immer workings for complex objects HOT 4
- Disable merge in easy peasy HOT 2
- HMR using Vite: module is not defined HOT 1
- Persist state with boolean false as default value? HOT 1
- Updating State HOT 2
- Broken in NextJS 13 with React 18 HOT 6
- Performance: Modifying data is too time-consuming HOT 3
- Property 'user' does not exist on type 'StateMapper<FilterActionTypes<{}>>' HOT 2
- Is it still maintained? HOT 2
- Next.js 13 example with Experimental App Directory HOT 3
- TypeError: proxy handler is null HOT 3
- Computed props which depend on computed props that consume store state don't update as expected
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from easy-peasy.