What is recommended way to determine if a Redux state empty array is the initial state or the result of an API call that returns an empty array? - reactjs

Let say I have an initial state tree that looks like this:
{
users: [],
items: []
}
In some cases, the result of calling the items API endpoint may result in a state tree like this:
{
users: [],
items: [
{itemId: 100, itemName: "Something100"},
{itemId: 101, itemName: "Something101"}
]
}
In other cases where there are not items to display, the state tree after an API call will be identical to the initial state tree.
Now in my component I'm using useEffect, something like this:
useEffect(() => {
if (items.length === 0) {
actions.loadItems().catch((error) => {
alert("Loading items failed! " + error);
console.log(error);
});
}
}, [items , actions]);
In this particular case, the length of items will be 0 in two cases: initial state or in case there are no results. If the API returns zero items and items.length === 0, then the action to call the API is executed repeatedly.
We really need a way of knowing that the empty array is the initial state or not. Of course I could change the state tree to something like:
{
users: {isLoaded: false, records: []},
items: {isLoaded: false, records: []},
}
That is going to add a bunch of overhead and refactoring and may not be most efficient/effective, so can someone give me a recommendation?

Unfortunately you will need some way of tracking the initialisation. If the issue is having to refactor then you can pull out this initialisation state into a higher order in the object from what you suggested. This will avoid refactoring so much:
{
isUsersLoaded: false,
isItemsLoaded: false,
users: [],
items: []
}
Another alternative is to init like this and check if users !== null etc.:
{
users: null,
items: null
}

Related

findIndex problem with Immer and Redux Starter Kit

I have a redux state that I need to update, it looks like this:
[
{
date: moment("2019-06-15").toObject(),
attendance: [
{
name: "Ruben Hensen",
membershipNumber: "2084700",
attending: true,
present: false
},
{
name: "Peter Petersen",
membershipNumber: "2084701",
attending: true,
present: false
},
{
name: "Karel Kootjes",
membershipNumber: "2084702",
attending: true,
present: false
},
{
name: "Niedaar Mennekes",
membershipNumber: "2084703",
attending: true,
present: false
},
]
},
...
...
...
]
My reducer tries to find the correct week so it can update it later but is unable to find the correct week inside the array.
TOGGLE_PRESENCE: (state, action) => {
console.log(state);
console.log(action);
const index = state.findIndex(week => week.date === action.payload.date);
console.log(index);
}
The problem is that I use Redux Starter Kit and it uses Immer inside reducers. If I run my reducer this is the result of the console logs.
It's not able to find the correct week.
Any tips or pointers would be appreciated. I don't really understand Immer or the Proxy objects it uses.
Edit:
#markerikson solved the problem. I changed this:
const index = state.findIndex(week => week.date === action.payload.date);
to this:
const index = state.findIndex(week =>moment(week.date).isSame(action.payload.date));
And now it finds the correct index.
The moment instances are not going to be === equal to each other. You probably need to use Moment's APIs for comparing them, like:
const index = state.findIndex(week => week.date.isSame(action.payload.date));
Note that this part doesn't have anything to do with Immer or Redux Starter Kit specifically - it's that different object instances are going to have different references.

Redux updating nested immutable data

I have an issue with updating the immutable redux and quite nested data. Here's an example of my data structure and what I want to change. If anyone could show me the pattern of accessing this update using ES6 and spread operator I would be thankful.
My whole state is an object with projects (key/value pairs - here as an example only one project) that are objects with its own key (and the keys are ids as well), arrays of procedures and inside the tasks:
{ 1503658959473:
{ projectName: "Golden Gate",
projectLocation": "San Francisco",
start:"22/09/1937",
id:1503658959473,
procedures:[
{ title: "Procedure No. 1",
tasks:[
{name: "task1", isDone: false},
{name: "task2", isDone: false},
{name: "task3", isDone: false}
]
}
]
}
}
What I'm willing to do is to update one single task 'isDone' property to 'true'. It's some kind of toggling the tasks. How can I return this state with that information updated?
The action creator pass this information to reducer:
export function toggleTask(activeProject, task, taskIndex) {
return {
type: TOGGLE_TASK,
payload: {
activeProject,
task,
taskIndex
}
};
}
You've run into a common issue with Redux. The docs recommend that you flatten your data structure to make it easier to work with, but if that's not what you want to do, I'd refer to this part of their docs.
Because both Object.assign() and the ...spread operator create shallow copies, you must go through each level of nest in your object and re-copy it.
Your code might look something like this...
function updateVeryNestedField(state, action) {
return {
...state,
procedures : {
...state.procedures,
tasks : {
return tasks.map((task, index) => {
if (index !== action.taskIndex) {
return task
}
return {
...task,
task.isDone: !task.isDone
}
}
}
}
}
}
I myself would create a new class called ProjectModel, which has a public method toggleTask that is able to update its task's status. The reducer state would be an object whose keys are project IDs and values are ProjectModel instances.

Understanding state in React

I am creating an app which displays and hides UI elements on the page based on checkbox value of a 'toggler' and checkbox values of the list elements which are created from this.state.items.
The app has an initial state set as:
state = {
items: [
{
id: 1,
name: 'Water',
isComplete: false
}, {
id: 2,
name: 'Salt',
isComplete: false
}, {
id: 3,
name: 'Bread',
isComplete: false
}
],
isChecked: false,
currentItem: '',
inputMessage: 'Add Items to Shopping Basket'
}
I created the following method which filters through items and returns all of the isComplete: false, and then I set the new state with these returned items.
toggleChange = (e) => {
this.setState({isChecked: !this.state.isChecked});
if (!this.state.isChecked) {
const filtered = this.state.items.filter(isComplete);
this.setState({items: filtered})
} else {
// display previous state of this.state.items
}
}
How do I come back to the 'Previous' state when I set 'toggler' to false?
If you only need to keep the default list then keep it out of state entirely
and just keep the filtered list in the state.
This way you could always filter the original list.
You can even consider filtering in the render method itself and not keeping the filtered list in state at all.
If you need to go back before a change was made (keeping history)
you could maintain another filtered list in your state.
I am not sure what you want to do but, if you implement componentWillUpdate() (https://facebook.github.io/react/docs/react-component.html#componentwillupdate) you get access to the next state and the current state.
-nextState will be what your state is going to be, and this.state is what it is now.
You could save this.state into another variable called this.oldState before it is updated, and then refer back to it.
Nonetheless, since state doesn't keep history of itself, you might consider approaching the problem differently.

When does it make sense to use Immutable.js in React?

I've read that Immutable.js only make sense if you have a deep tree comparison to make. So I am assuming in the case where my application state looks like this:
const taskList = [
{
name: 'task 1',
priority: '1',
isDone: false
},
{
name: 'task 2',
priority: '1',
isDone: false
},
{
name: 'task 3',
priority: '1',
isDone: false
}
];
It's not very useful and it should look something like this to make it useful:
{
"stuff": {
"onetype": [
{"id":1,"name":"John Doe"},
{"id":2,"name":"Don Joeh"}
],
"othertype": {"id":2,"company":"ACME"}
},
"otherstuff": {
"thing": [[1,42],[2,2]]
}
}
So that we can use shallow comparison like:
shouldComponentUpdate(nextProps) {
return (this.props.name !== nextProps.name || this.props.priority !== nextProps.priority || this.props.isDone !== nextProps.isDone );
}
instead of traversing the tree, which is expensive. But otherwise, is there any reason to use Immutable.js? The above works with taskList just fine. In which case is this really needed?
EDIT:
I have been using lodash and I just heard that lodash takes mutability in mind, but I am not sure how it does the same thing as Immutable.js without immutables.
toggleTask(task) {
const found = _.find(this.state.taskList, task => task.name === task);
found.isDone = !found.isDone;
this.setState({ taskList: this.state.taskList });
}
If your taskList which you are rendering is large and elements get updated very frequently, using immutableJS objects will prevent you from renrendering all the list elements.
For example. Lets say, there is a list of 1000 tasks, which is rendered on the page.Now you made another server poll (or push), and you get the same 1000 tasks with one of the tasks property isDone changed. So, if you simply replace the old tasksList array with new tasksList array, all react components will rerender as every item in the list is a new element and shallow compare fails and this, all lifecycle methods of each list item component gets triggered. But if your taskList was an Immutable List, then you do a taskList.mergeDeep(newTaskList), only the reference of the List and the one element that has updated is changed. Thus every other list item will not go past shallow compare except the task item that has changed.

How can I get an item in the redux store by a key?

Suppose I have a reducer defined which returns an array of objects which contain keys like an id or something. What is the a redux way of getting /finding a certain object with a certain id in the array. The array itself can contain several arrays:
{ items:[id:1,...],cases:{...}}
What is the redux way to go to find a record/ node by id?
The perfect redux way to store such a data would be to store them byId and allIds in an object in reducer.
In your case it would be:
{
items: {
byId : {
item1: {
id : 'item1',
details: {}
},
item2: {
id : 'item2',
details: {}
}
},
allIds: [ 'item1', 'item2' ],
},
cases: {
byId : {
case1: {
id : 'case1',
details: {}
},
case2: {
id : 'case2',
details: {}
}
},
allIds: [ 'case1', 'case2' ],
},
}
Ref: http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
This helps in keeping state normalized for both maintaining as well as using data.
This way makes it easier for iterating through all the array and render it or if we need to get any object just by it's id, then it'll be an O(1) operation, instead of iterating every time in complete array.
I'd use a library like lodash:
var fred = _.find(users, function(user) { return user.id === 1001; });
fiddle
It might be worth noting that it is seen as good practice to 'prefer objects over arrays' in the store (especially for large state trees); in this case you'd store your items in an object with (say) id as the key:
{
'1000': { name: 'apple', price: 10 },
'1001': { name: 'banana', price: 40 },
'1002': { name: 'pear', price: 50 },
}
This makes selection easier, however you have to arrange the shape of the state when loading.
there is no special way of doing this with redux. This is a plain JS task. I suppose you use react as well:
function mapStoreToProps(store) {
function findMyInterestingThingy(result, key) {
// assign anything you want to result
return result;
}
return {
myInterestingThingy: Object.keys(store).reduce(findMyInterestingThingy, {})
// you dont really need to use reduce. you can have any logic you want
};
}
export default connect(mapStoreToProps)(MyComponent)
regards

Resources