Description
The way my state is structured right now is a bit messy.
I have multiple top level keys which are related to the currently logged in user and some keys
which aren't related to the logged in user, e.g.:
{
// logged in user
me: {
id: 'olalonde',
username: 'olalonde',
name: 'Olivier Lalonde',
email: 'olalonde@gmail.com',
token: 'some-json-webtoken',
},
// lgoged in user's files
myFiles: {
pagination: { total: 6, limit: 3, page: 1 },
data: [
{ id: 'a', filename: 'myfile.txt' },
{ id: 'b', filename: 'myfile.doc' },
{ id: 'c', filename: 'myfile.jpg' },
],
},
// lgoged in user's events
myEvents: {
pagination: { total: 2, limit: 3, page: 1 },
data: [
{ id: 'a', text: 'Some event...' },
{ id: 'b', text: 'Some other event...' },
],
},
// loaded when user browses a user's profile
activeUserProfile: {
id: 'billgates',
username: 'billgates',
name: 'Bill Gates',
},
// loaded when user browses a user's profile
activeUserFiles: {
pagination: { total: 2, limit: 3, page: 1 },
data: [
{ id: 'd', filename: 'somefile.txt' },
{ id: 'e', filename: 'somefile.doc' },
],
},
}
It's a bit messy and I was looking for suggestions to refactor.
When logging out I'd have to check for a LOGOUT
action in the me
, myFiles
and myEvents
reducers which is not really maintainable if I keep adding
stuff. Another option would be to put everything related to the logged in user
under a me
key/reducer and clear me
when LOGOUT
is dispatched, e.g.:
{
// logged in user
me: {
user: {
id: 'olalonde',
username: 'olalonde',
name: 'Olivier Lalonde',
email: 'olalonde@gmail.com',
token: 'some-json-webtoken',
},
files: {
pagination: { total: 6, limit: 3, page: 1 },
data: [
{ id: 'a', filename: 'myfile.txt' },
{ id: 'b', filename: 'myfile.doc' },
{ id: 'c', filename: 'myfile.jpg' },
],
},
events: {
pagination: { total: 2, limit: 3, page: 1 },
data: [
{ id: 'a', text: 'Some event...' },
{ id: 'b', text: 'Some other event...' },
],
},
},
activeUser: {
// logged in user's events
// loaded when user browses a user's profile
user: {
id: 'billgates',
username: 'billgates',
name: 'Bill Gates',
},
files: {
pagination: { total: 2, limit: 3, page: 1 },
data: [
{ id: 'd', filename: 'somefile.txt' },
{ id: 'e', filename: 'somefile.doc' },
],
},
}
}
A bit cleaner... But there will be some duplication in my files
and user
reducers. Another option would be to normalise the whole thing:
{
me: 'olalonde',
users: {
olalonde: {
id: 'olalonde',
username: 'olalonde',
name: 'Olivier Lalonde',
email: 'olalonde@gmail.com',
token: 'some-json-webtoken',
files: {
pagination: { total: 6, limit: 3, page: 1 },
entitites: ['a', 'b', 'c'],
},
events: {
pagination: { total: 2, limit: 3, page: 1 },
entitites: ['a','b'],
},
},
billgates: {
id: 'billgates',
username: 'billgates',
name: 'Bill Gates',
files: {
pagination: { total: 2, limit: 3, page: 1 },
entities: ['d','e'],
},
},
},
files: {
a: { id: 'a', filename: 'myfile.txt' },
b: { id: 'b', filename: 'myfile.doc' },
c: { id: 'c', filename: 'myfile.jpg' },
d: { id: 'd', filename: 'somefile.txt' },
e: { id: 'e', filename: 'somefile.doc' },
},
events: {
a: { id: 'a', text: 'Some event...' },
b: { id: 'b', text: 'Some other event...' },
}
}
Now this kind of normalisation leads to new problems. How do I garbage collect
the state? e.g. If a user browses thousands of profiles, I don't want to end up
with a state that has thousands of entities. I could issue an { type: 'RESET_PROFILE_DATA', user_id: 'billgates' }
action when a user's profile component is
unmounted. But how does my reducer know which piece of data can be safely removed (e.g. the logged in user state shouldn't be cleared if the user navigates outside its own profile page). We
could add special cases everywhere but it doesn't feel maintainable. Another problem is that there
could be two separate components which are using the same piece of state and it'd be hard to tell.
I guess we could keep a garbage collection counter for every entity which gets incremented when the data is needed and decremented when the data can be garbage collected, etc. But that can get quite complex.
Also, what if I need to display a user's file list simultaneously in two different components and both have their own pagination. I could have filesForComponentA
and filesForComponentB
instead of files
?
Anyways, I've got more ideas but would be curious how people typically handle this situation.