Managing redux state for a list of items - reactjs

Prior to this, I was managing general Redux state as follows:
for example I was setting isRequestTags from a reducer.
But now I'm facing another challenge:
Suppose I have a list of tags, for each tag there can be some states defined like isPrimaryTag.
How can I define states for a list of items which have a common attribute?

If you have a list of tags, and each tag has, say, a name and a flag, then you can't "refactor" that out in any meaningful way, e.g.,
tags: [
{ name: 'foo', isPrimary: true },
{ name: 'bar', isPrimary: false }
]
If the common attributes are themselves an object, particularly a large one, you'd use normal state-shape practices as outlined in the Redux docs.
For example, if each tag had something like this:
tagInfo: {
isPrimary: true,
group: 'whatever',
somethingElse: { etc: 'etc' }
}
and multiple tags had the same value, you'd provide an ID/index:
tagInfos: [
{
isPrimary: true,
group: 'whatever',
somethingElse: { etc: 'etc' }
},
{
isPrimary: true,
group: 'whatever',
somethingElse: { etc: 'etc' }
}
]
tags: [
{ name: 'foo', tagInfoIndex: 0 },
{ name: 'bar', tagInfoIndex: 1 }
// etc
]
All that said, I'm not entirely sure if that's what you're asking.

Related

Spread operator redorders array alphabetical

I have a reducer which updated a field's value inside an array like so:
return {
...state,
[action.field]: { ...state[action.field], value: action.payload }
};
This works fine but my state is an array of named objects (I think these are called object literals) like so:
export const state: any = {
"account_name": {
label: 'Account Name',
type: 'text',
name: 'account_name',
value: '',
required: true
},
"account_website": {
label: 'Website',
type: 'url',
name: 'account_website',
value: '',
required: false
},
My reducer works fine and updates the object as required, however it reorders the entire array in alphabetical order.
However if I change my update function to the following it works fine:
state[action.field] = action.payload
return state
I ended up changing my object (string->value mapping) to a regular array like so:
export const newAccountFields: any = [
{
label: 'Account Name',
type: 'text',
name: 'account_name',
value: '',
required: true,
},
{
label: 'Contact',
type: 'contacts',
name: 'account_contact',
value: '',
required: true,
},
Then when using the reducer my order is unchanged

How to update a same field in all objects of a same type in normalised cache?

I'm using Apollo Client 3 in a react project.
I have a data structure like this:
ROOT_QUERY
getCollection {
__typename: 'Collection',
_id: '123'
tagColorMap: [
{__typename: 'Tag',
name: 'tag1',
color: "#673ab7",
count: 3},
{__typename: 'Tag',
name: 'tag2',
color: '#f44336',
count: 1},
...
]
entries: [
{
__typename: 'Entry',
_id: 'asd'
tags: [tag1, tag2, tag3]
},
{
__typename: 'Entry',
_id: 'qwe'
tags: [tag2, tag3]
},
...
}
}
the data are normalised in the cache.
ROOT_QUERY
getCollection{
"__ref": "Collection:123"
}
Collection:123{
_id: '123'
tagColorMap: [
{__typename: "Tag",
name: "tag1",
color: "#673ab7",
count: 3},
{__typename: "Tag",
name: "tag2",
color: "#f44336",
count: 1},
...
]
entries: [
{
__ref: "Entry:asd"
},
{
__ref: "Entry:qwe"
},
...
]
Entry:asd {
_id: 'asd'
tags: ['tag1', 'tag2', 'tag3']
},
Entry:qwe {
_id: 'qwe'
tags: ['tag2', 'tag3']
},
I performed a mutation, which renames one of the tag, say 'tag1' -> 'tag11', which returns the new tagColorMap;
now I want to change all 'tag1' into 'tag11' in the cache.
I have gone through the official doc and googled it for a while, but still can't find a way to do this.
refetching won't work because the time between the mutation is done and the refetch is done, all the entries that still have tag 'tag1' don't have a corresponding colour in the colour map, it will fallback to default colour, then back to the original colour after the refetch is done.
another way might be that to let the server return the entire collection after the mutation, which is quite a lot of data
so that's why I would like to rename all 'tag1' into 'tag11' in all 'entry" objects directly in cache, but I couldn't find a way to do this... Could anyone help me with this?
thank you very much in advance!

difference between knobs and controls in Storybook?

I am new to the storybook. When I go through the documentation and videos about the storybook I read about knobs Addon. Knobs addon and control looks similar. What is the difference between those two things?
Controls were introduced with Storybook version 6. They replace knobs for most of the use cases. However, there may be some edge cases were you still want to use knobs for dynamic values. For example, see this Github discussion on this topic: https://github.com/storybookjs/storybook/issues/11984
controls addon is a companion to the docs addon so it interfaces with the ArgsTable which by itself is designed to automatically extract your components' propTypes & defaultProps (although I found this not to work)
So, with Knobs you define each prop (which you wish to be dynamic) yourself, manually, and this requires some more manual sync when your component changes and also more work, and also Knobs variables definitions might be scattered all across your story's file, where controls are all defined in one place, though the same "order" can also be done with Knobs, it does not enforces it (for good reasons).
If you want to have an interactive propTypes documentation for your components, then I suggest using controls with addon-docs, and I've been using knobs for years, but that's it, it's time to upgrade.
If, for some reason, your component's propTypes where not auto-detected (in the story) then you can define then (with controls) like so:
import Alert from './';
export default {
title: 'General/Alert',
component: Alert,
parameters: {
controls: { expanded: true }, // Show full documentation for each property
},
argTypes: {
type: {
description: 'Alert.Types',
defaultValue: Alert.Types.WARNING,
table: {
type: {
summary: 'string',
},
defaultValue: {
summary: Alert.Types.WARNING,
},
},
options: Alert.Types,
control: {
type: 'select', // for selecting between the array of options above
},
},
title: {
defaultValue: '',
table: {
type: {
summary: 'string',
},
},
description: 'An optional title',
control: {
type: 'text',
},
},
onClose: {
table: {
type: {
summary: 'func',
},
},
description: '× button click callback',
control: { type: null },
},
children: {
description: 'The message body (mandatory)',
type : {
required: true,
},
table: {
type: {
summary: 'node',
},
},
control: { type: null },
},
},
}
//...export your story...
Notes:
How to migrate dynamic knobs to controls?

How do combine nested reducers?

In my application, I want to get the following state structure:
{
appName: "Combine Reducers App",
fruits: {
apples: [
{ id: 0, name: "apple1", saved: true },
{ id: 1, name: "apple2", saved: true }
],
oranges: [
{ id: 0, name: "orange1", saved: true },
{ id: 1, name: "orange2", saved: true }
],
loaded: false,
saved: true
}
}
I need individual reduxers to be responsible for managing the state of apples and oranges.
Can you tell me how to type and combine nested reducers?
Source code: https://stackblitz.com/edit/react-redux-ts-nested-reducers?file=store.ts
If you click the test orange reducer button, the orange reducer will not work.
The nesting need the same as I described above and orange reducer must work on action

Database: flat vs nested data

I would like to store data in a database that can be laid out nested like
[
{
id: 'deadbeef',
url: 'https://lol.cat/1234',
revisions: [
{
id: '1',
title: 'foo',
authors: ['lol', 'cat'],
content: 'yadda yadda',
// ...
},
{
id: '2',
title: 'foo',
authors: ['lol', 'cat'],
content: 'yadda yadda bla',
// ...
},
// ...
]
},
// ...
]
(One can imagine more levels here.)
Alternatively, the same data could be organized flat like
[
{
documentId: 'deadbeef',
url: 'https://lol.cat/1234',
id: '1',
title: 'foo',
authors: ['lol', 'cat'],
content: 'yadda yadda',
// ...
},
{
documentId: 'deadbeef',
url: 'https://lol.cat/1234',
id: '2',
title: 'foo',
authors: ['lol', 'cat'],
content: 'yadda yadda bla',
// ...
},
// ...
]
with basically only the leaves of the approach above stored, along with all the information belonging to them.
Typical requests would be:
Give all revisions of document deadbeef.
Give me revision 6 of document caffee.
Is either one of the approaches obviously better? What are advantages/disadvantages of either approach?
Your second schema is a denormalized version of the first. It might be useful to compare a more relational approach:
{
documents: [
{
id: 'deadbeef',
url: 'https://lol.cat/1234',
// ...
},
// ...
],
revisions: [
{
id: '1',
documentId: 'deadbeef'
title: 'foo',
authors: ['lol', 'cat'],
content: 'yadda yadda',
// ...
},
{
id: '2',
documentId: 'deadbeef',
title: 'foo',
authors: ['lol', 'cat'],
content: 'yadda yadda bla',
// ...
},
// ...
]
}
The nested approach suffers from a problem called access path dependence. Basically, by assuming a preferred hierarchy, it makes queries that require a different hierarchy more difficult.
The denormalized version can suffer from update anomalies, which means partial updates can put the database into an inconsistent state.
The relational approach, on the other hand, doesn't favor any hierarchy, thereby supporting ad-hoc querying, and normalization helps to eliminate update anomalies. RDBMSs also incorporate numerous integrity checks and constraints to ensure the validity of data.

Resources