Array getting undefined on state change on Redux - javascript

I am trying to mutate state by running filter and map methods, when I am returning my array the state is becoming undefined.
const getAllApprovedProviderReducer = (state=defaultState, action) => {
if(action.type === GET_ALL_APPROVED_PROVIDER) {
return {
...state
}
}
if(action.type === FILTER_BY_FINANCIAL_SERVICES) {
console.log(action.payload)
return {
...state,
success: state.success.map((item, index)=> {
item.servicesOffered.filter(i=>{
if(i===action.payload) {
return item
}
else {
// console.log(state.success);
return item
}
})
})
}
}
return state;
}
At the presentation component the state is changed to undefied.
What I am doing wrong to make state undefined.

From my comment:
The function you pass to .map does not return anything, hence every value is mapped to undefined. return doesn't cross function boundaries and the function you pass to .filter should return a boolean value.
If you only want to keep the objects which offer a specific service, then you can do that the following way:
state.success.filter(item => item.servicesOffered.includes(action.payload))
.filter will remove every element for which the callback returns false.
Using .map here doesn't make sense since it will always be a 1:1 mapping between input and output. If you want to reduce the number of elements you have to use .filter.
And while it's possible to use .filter to test for the presence of a value in an array, there are more specific methods for that, such as .includes and .some.

Related

When pushing a new value inside an array it gets totally override - VUEX

Hello so I am creating a filter search and I 'm trying to collect all the key (tags) that the user press, inside an array, however every time that a new value is push it does override the entire array. So I tried a couple of things, like spread syntax, concat, etc. But with no luck.
So my action looks like this:
const setCurrentFilters = async (context, payload) => {
if (payload) {
context.commit('setCurrentFilter');
}
}
My state
state:{
filters: JSON.parse(sessionStorage.getItem('currentFilters') || '[]'),
}
The mutation
setCurrentFilter(state, payload) {
state.filters.push(payload);
sessionStorage.setItem('currentFilters', JSON.stringify(payload));
}
And my getter
currentFilters(state) {
return state.filters;
},
Thank you in advance for any help : )
This is simply because you set const filters = []; which means that the next condition if (filters.length) will always return false (as you just created this array) and therefore the else statement will execute.
in the else statement you basically push the new payload to the empty array you just initialized - which makes your array always hold only the new value
i believe that you just need to remove the const filters = []; line, and access the filters property that exists in your state

How to assign an object from an array to an arrow function?

I am trying to assign a variable with the properties of an object from an array in my redux state. I am trying to loop through the array of objects and assign the variable when the ID of the item matches up to the ID that I am searching for.
I have been trying anything I can from nested if statements, multiple returns, I cannot seem to figure this out.
Currently, this is what I have.
const currItemProps = () => {
this.props.todos.find((todo) => {
(todo.id === this.props.itemID) ?
{ todo } : null
}
);
};
todos is my array I am searching for and the itemID is the ID I am lookking for (both are pieces of redux state).
I am trying to open a modal on the press of a todo that has the properties of the todo. Hence I am trying to assign a variable in my modal file with all of the properties of its current todo (the object from the array).
The find function expects that you'll return True when you find your item.
also, you need to specify a 'return' statement.
const currItemProps = () => {
return this.props.todos.find((todo) => todo.id === this.props.itemID);
};
In case you directly want to return model
const currItemProps = () => {
this.props.todos.find((todo) => {
(todo.id === this.props.itemID) ?
<ComponentName todoProps={todo}/> : null
}
);
};
then you can use the same in render method like {currentProps}

Reactjs filter state by key and value

React Newbie here.
I'm trying to match the value of a specific id located in my state, so I can change some value before updating the database via my api.
The state is
state = {
library: []
}
and then with when the axios changes the state the array looks something like:
0:{_id:"", time:"", value:""},
2:{_id:"", time:"", value:""}
When I run console.log, it reads it like this.
(2) [{…}, {…}]0: {_id: "5c82803ad634ea0bebfb3eff", time: "2019-03-08T14:46:18.663Z", value:""}1: {_id: "5c827fb9d634ea0bebfb3efe", time: "2019-03-08T14:44:09.818Z", value:""}
So basically when I type in a specific input field, identified by it's _id, I need to update the value state of that specific state.
Here's the code I have written so far. _id is the unique key of the input field and event value what I'm typing.
updateRead = (_id, event) => {
console.log(_id);
console.log(event.target.value)
this.setState(?????)
};
Help would be much appreciated!
Cheers
You can use the array map method on the library array in your state, and just return the elements as is if the _id doesn't match, and update the value if the _id does match.
updateRead = (_id, event) => {
const { value } = event.target;
this.setState(prevState => ({
library: prevState.library.map(read => {
if (read._id !== _id) {
return read;
}
return {
...read,
value
};
})
}));
};
Two of the fundamental rules of state in React are:
Never modify state directly
Use the callback version of setState when you're setting state based on existing state (more)
Separately, you can't access properties on the synthetic event after the event handler has returned, so you'll want to grab the value before calling setState (since the call to your callback will be asynchronous).
Within the callback, you copy the array, find the relevant object, copy it, and set the value.
So:
updateRead = (_id, event) => {
const {value} = event.target;
this.setState(({library}) => ({
library: library.map(entry => entry._id === _id ? {...entry, value} : entry)
}));
};
map creates a new array from the previous array's entries. The callback returns a new object if the IDs match (using property spread notation) with an updated value, or the original object (since we don't have to copy objects we aren't modifying).

In Reactjs, how to make a prop function do separate tasks and return their values according to the required condition

I have a function in my parent component which is sent to the child components as a prop.In one of my child component,I want the same function(which was sent as a prop from the parent component) to be run twice.The first will be run with some arguments and return the value from that particular child component.The second will be to pass the same function(which came from the parent component,not the one executed in this component) to a separate component as a props again.So my function is:
//Function defined in the parent component and sent as props to the child components
handleShelfChange = (bookOnChange, newSehlf) => {
!this.isTheBookNew(bookOnChange) && this.setState(state => {
let newBooks = state.books.map(book =>
{ if (bookOnChange.id === book.id)
{ book.shelf = newSehlf; }
return book;
});
return {
books: newBooks
};
}
);
BooksAPI.update(bookOnChange, newSehlf);
};
I am sending this function to one of the child component as shown below:
<BookSearch
books={this.state.books}
handleShelfChange={this.handleShelfChange}/>
Now, in my BookSearch Component,I need to perform 2 actions:
1) Use handleShelfChange() method and return values from BookSearch Component.
2) Pass handleShelfChange() method to another component as props which will use the same method again and return some values.
So,I am facing some issues in the first point, I am using the function as a callback to this.setState as
this.setState({ books : book_search }, this.check()); // callback function to this.setState
and the callback function is as shown below:
check() {
let parent_books = this.props.books;
let shelf;
console.log(this.state.books)
const book_result = this.state.books.map((book) => {
const parent = parent_books.find(parent => parent.title === book.title );
if(parent) {
console.log(parent);
book.shelf = parent.shelf;
let shelfChange = this.props.handleShelfChange(book,book.shelf) //Call to the
//function sent as props from the parent component
shelf = shelfChange
}
console.log(shelf) // undefined
return [book,shelf];
})
//console.log(book_result);
this.setState({books: book_result})
console.log(this.state.books)
}
So,I need to run the handleShelfChange() function here for all the books that satisfy the if condition and return it from the check method(the callback method). So, I tried to declare a variable outside the if condition and assign the output of the function to that variable(declared outside the if condition) .I also need to return the each book from the map function satisfying the condition.So,I have used an array to return both the values. But as I have mentioned in the comment, console.log(shelf) return an empty array.It shows undefined.What is the correct way to get both the values from the callback function? Can anyone please suggest me a solution?
Your problem arises because you are using the setState callback syntax incorrectly. The second argument to setState is a callback function you are just executing the function
You need to write it like
this.setState({ books : book_search }, this.check);
Also console.log() after setState doen't show you the updated state values, you should write it in the callback
this.setState({books: book_result}, () => {console.log(this.state.books)})
Also make sure that you are returning the appropriate value from the handleSelfChange function

Error: Attempting to set key on an object that is immutable and has been frozen

I am having a rather hard time tracking down the issue related to this error, obviously the implication is I'm trying to update an immutable object. Oddly, I have used this implementation before without a hitch which is why I am finding this behaviour so perplexing.
Below is the method I am using, it simply alters the object of an array, changing the property and returns the updated state. As referenced here, using prevState appears to be the optimal way for getting around immutability.
onCheck = (id) => {
this.setState((prevState) => {
prevState.options[id].checked = !prevState.options[id].checked;
return {
options: prevState.options,
dataSource: prevState.cloneWithRows(prevState.options)
};
});
}
I have also tried a number of variations of copying the prevState, however it is still giving me the same immutability error. It appears as if it still references rather than duplicates the prevState.
onCheck = (id) => {
this.setState((prevState) => {
let options = [...prevState.options];
options[id].checked = !options[id].checked;
return {
options: options,
dataSource: this.state.cloneWithRows(options)
};
});
}
I eventually found a solution, it appears I needed to copy not just the array but each element of the array. As the individual elements / objects of the array are still immutable / frozen.
onCheck = (id) => {
this.setState((prevState) => {
const newOptions = prevState.options.map((option, index) => {
let copyOption = {...option};
if (id == index) {
copyOption.checked = !copyOption.checked;
}
return copyOption;
});
return {
options: newOptions,
dataSource: this.state.dataSource.cloneWithRows(newOptions)
};
});
}
In your question, you acknowledge that you are mutating the state, which you cannot do. A fix for this is to clone prevState.options before fiddling with it.
eg.
var options = Object.assign({}, prevState.options)
options[id].checked
return {
options,
...
As your reference pointed out, you should not directly mutate your data like you do
prevState.options[id].checked = !prevState.options[id].checked;
In your link, they return a new array by concat or by "spread syntax" (...). So i suggest you to copy your prevState first and mutate it
let state = [...prevState.options];
state[id].checked = !state[id].checked;
...

Categories