React native push with multiple key and value - reactjs

I have a group of checkboxes, whenever I select a checkbox I need to push an array of data, like { 'index':1, 'photo':'sample.png' } to state, and whenever I unselecting the checkbox, I need to remove it from the state. after I need to loop through the state to get index and photo to be used
handleSelection = async (media, index, isSelected) => {
alert(index);
if (isSelected == true) {
this.state.mediaSelected.push(media.photo);
} else {
this.state.mediaSelected.splice(this.state.mediaSelected.indexOf(media.photo), 1);
}
console.warn(this.state.mediaSelected);
}
this is working for single value without the key, is there any way to push it with key and value?

You should always update state with this.setState in your case would be something like this:
handleSelection = async (media, index, isSelected) => {
alert(index);
if (isSelected == true) {
this.setState({
mediaSelected: this.state.mediaSelected.push({
index,
photo: media.photo
})
});
} else {
this.setState({
mediaSelected: this.state.mediaSelected.splice(this.state.mediaSelected.indexOf(media.photo), 1)
});
}
console.warn(this.state.mediaSelected);
}

Try this:
Sorry I am working as well as answering your question so it is taking time.
handleSelection = async (media, index, isSelected) => {
let selectPhotosObj = this.state.selectPhotosObj || [];
if (isSelected == true) {
const data = { index, photo: media.photo };
//this.state.selectedPhotoObj will be the container for your object.
selectPhotosObj.push(data)
//need to set the new Array of Object to the state.
this.setState({ mediaSelected: media.photo, selectPhotosObj });
} else {
const removedPhoto = this.state.mediaSelected.filter(value => value !== media.photo);
selectPhotosObj = this.state.selectedPhotosObj.filter(value => value.index !== index);
this.setState({
mediaSelected: removedPhoto,
selectPhotosObj
})
}
console.warn(selectPhotosObj);
}

Related

React setState opposite boolean in map function not changing state

I created a simple to-do list in ReactJS. It loads components for to-do items stored in a file "TodoData.js", data is stored as followed in that file:
const todosData = [
{
id:1,
text: "Take out the thrash",
completed: true
},
{
id:2,
text: "Grocery shopping",
completed: false
},
App.js uses a TodoItem.js component to render each to-do item with a map function. TodoItem.js uses conditional rendering:
if (props.item.completed == true) {
return (
<div className="todoclassDone">
<input type="checkbox"
onChange={ () => props.handleChange(props.item.id)}/>
<p className="lalatext"><del>{props.item.text}</del></p>
</div>
)
}
else { .... //same code as above but with other className.
Within App.js I use the TodoItem.js component to render each item in TodoData with a map function; if the data.completed = true background is green, else background is red.
Problem: However, the handleChange(id) function in App.js is not working properly. I loop through all objects in todosData; if the id is similar to the id of checkbox which the user clicked it should change to the opposite boolean value using todo.completed = !todo.completed However, when running this code nothing is happening. The handleChange function:
handleChange(id) {
this.setState(prevState => {
const updatedTodos = prevState.todos.map(todo => {
if (todo.id == id) {
todo.completed = !todo.completed;
}
return todo
})
Extra info: Above mentioned problem is especially weird because if I change the boolean value of the checkbox clicked by the user to either false or true it does work. This does not result in the desired behaviour because now I am only able to change the todo.completed once from false to true. ; In this case the handleChange function would look as follows:
handleChange(id) {
this.setState(prevState => {
const updatedTodos = prevState.todos.map(todo => {
if (todo.id == id) {
todo.completed = true;
}
return todo
})
Any help is highly appreciated, thanks in advance! :-)
Ciao, you could try to copy state on an array, modify array and set the state wht updated array. Something like:
handleChange(id) {
let result = this.state.todos;
result = result.map(todo => {
if (todo.id === id) todo.completed = !todo.completed;
return todo;
})
this.setState({todos: result})
}
You should return the new state in your setState callback, with the new state object.
Example todo component with relevant code:
class Todo extends Component {
state = {
todos: todosData,
}
handleChange(id) {
this.setState(prevState => {
const todos = prevState.todos.map(todo => {
if (todo.id === id) {
return {
...todo,
completed: !todo.completed
}
}
return todo;
});
return { todos };
}
}

How to select multiple checkbox in reactjs?

The problem is that I need to pass array of object , I can not get my accepted data .My accepted data will be this format. However, user can remove by using unchecked.
{
notification : {group:["can_view","can_create", "can_delete","can_update","can_upload","can_download"],
topGroup:["can_view","can_create", "can_delete","can_update","can_upload","can_download"}
}
handleAllChecked = (id, role, data) => event => {
let fruites = this.state.fruites;
fruites
.filter(f => f.groupId === id)
.forEach(fruite => {
if (event.target.checked === true) {
fruite.isChecked = event.target.checked;
console.log(data);
} else {
fruite.isChecked = false;
}
});
this.setState({ fruites: fruites });
};
here is my codesanbox , You can easily understand
https://codesandbox.io/s/fragrant-http-v35lf

How to solve Error Use object destructuring prefer-destructuring - React

I am stuck with an ugly issue which I am unable to resolve. I am beginner in React.
This is my Code
handleCheckChildElement(event) {
let items = this.state.items;
items.forEach(items = () => {
if(items.value === event.target.value) {
items.isChecked = event.target.checked;
}
});
this.setState({ items });
}
This is the image of the error -
Use below code for line #55 :
let {items}= {...this.state};
Read more here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring
Your code can be improved to something like below. Please find relevant comments in the code below for your better understanding
handleCheckChildElement(event) {
const { items } = this.state; //extract state values like this to a const variable
const newItems = items.map(item => { //do map on items because map returns a new array. It’s good practice to use .map than forEach in your case
if(item.value === event.target.value) {
item.isChecked = event.target.checked;
return item; //return updated item object so that it will be pushed to the newItems array
}
return item; // return item because you need this item object as well
});
this.setState({ items: newItems}); //finally set newItems array into items
}
handleCheckChildElement(event) {
const items = this.state.items;
const filtered = items.filter(item => item.value === event.target.value)
.map(item => item.isChecked = event.target.checked) ;
this.setState({items : [...filtered] );
}

Comparing PrevProps in componentDidUpdate

I am trying to detect when a prop has changed inside componentDidUpdate of a mounted component. I have a test (refreshData in the code below) that is working fine. Is it possible to SOMEHOW pass props in a way that aren't detected by componentDidUpdate(prevProps)?
In component.js:
componentDidUpdate(prevProps){
//works fine
if ( this.props.refreshData !== prevProps.refreshData ) {
if ( this.props.refreshData )
this.refreshData();
}
//these two arent calling
if ( this.props.selectedCountries !== prevProps.selectedCountries ) {
if ( this.props.selectedCountries )
console.log('updated selected countries');
}
if ( this.props.selectedLocations !== prevProps.selectedLocations ) {
console.log('updated selected locations');
}
}
and in App.js passing the props like:
selectLocation = (id, type, lng, lat, polydata, name, clear = false) => {
//console.log(type);
//console.log(lng);
//console.log(lat);
//console.log(polydata);
let selectedType = 'selected' + type;
let previousState = [];
if (clear) {
this.setState({
selectedCountries: [],
selectedLocations: [],
selectedServices: [],
selectedPoints: [],
mapCenter: [lng, lat],
locationGeoCoords: [polydata]
})
previousState.push(id);
} else {
previousState = this.state[selectedType];
if (previousState.indexOf(id) === -1) {
//push id
previousState.push(id);
} else {
//remove id
var index = previousState.indexOf(id)
previousState.splice(index, 1);
}
}
if (type === "Countries") {
this.setState({
selectedCountries: previousState,
refreshData: true,
})
} else if (type === "Locations") {
this.setState({
selectedLocations: previousState,
refreshData: true
})
} else if (type === "Points") {
this.setState({
selectedPoints: previousState,
refreshData: true
})
}
}
render() {
return (
<component
selectedCountries={this.state.selectedCountries}
selectedLocations={this.state.selectedLocations}
refreshData={this.state.refreshData} />
}
}
Hi :) as noted in my comment, the issue is in your App.js file - you are mutating an array. In other words, when you THINK you are creating a new array of selected countries to pass down, you are actually updating the original array, and so when you go to do a comparison you are comparing the two exact same arrays ALWAYS.
Try updating your App.js like so -
selectLocation = (id, type, lng, lat, polydata, name, clear = false) => {
//console.log(type);
//console.log(lng);
//console.log(lat);
//console.log(polydata);
let selectedType = 'selected' + type;
let previousState = [];
if (clear) {
this.setState({
selectedCountries: [],
selectedLocations: [],
selectedServices: [],
selectedPoints: [],
mapCenter: [lng, lat],
locationGeoCoords: [polydata]
})
previousState.push(id);
} else {
previousState = [].concat(this.state[selectedType]);
if (previousState.indexOf(id) === -1) {
//push id
previousState.push(id);
} else {
//remove id
var index = previousState.indexOf(id)
previousState.splice(index, 1);
}
}
if (type === "Countries") {
this.setState({
selectedCountries: previousState,
refreshData: true,
})
} else if (type === "Locations") {
this.setState({
selectedLocations: previousState,
refreshData: true
})
} else if (type === "Points") {
this.setState({
selectedPoints: previousState,
refreshData: true
})
}
}
render() {
return (
<component
selectedCountries={this.state.selectedCountries}
selectedLocations={this.state.selectedLocations}
refreshData={this.state.refreshData} />
}
}
The only difference is the line where you set previousState - I updated it to be
previousState = [].concat(this.state[selectedType]);
By adding the [].concat I am effectively creating a NEW array each time and so then when you apply your changes to the array via push/splice you will be only modifying the NEW array. Then the comparison will work properly once you pass it down as props :)
For your reading interest, I found a post that talks about this a bit: https://medium.com/pro-react/a-brief-talk-about-immutability-and-react-s-helpers-70919ab8ae7c
selectedCountries and selectedLocations are array objects. The reference of it never changes. Instead check for the length.
componentDidUpdate(prevProps){
if ( this.props.refreshData !== prevProps.refreshData ) {
if ( this.props.refreshData )
this.refreshData();
}
if ( this.props.selectedCountries.length > prevProps.selectedCountries.length ) {
if ( this.props.selectedCountries )
console.log('updated selected countries');
}
if ( this.props.selectedLocations.length > prevProps.selectedLocations.length ) {
console.log('updated selected locations');
}
}
In the code snippet above, you seem to be making changes to this.state directly. State should be immutable. Always make sure, you concat to add and filter to delete the elements as they create a new array instead of mutating the original array in the state. I would do something in these lines.
Also it is a good practice to capitalize the component name.
selectLocation = (id, type, lng, lat, polydata, name, clear = false) => {
//console.log(type);
//console.log(lng);
//console.log(lat);
//console.log(polydata);
let selectedType = "selected" + type;
let previousState = [];
let updatedData = [];
if (clear) {
this.setState({
selectedCountries: [],
selectedLocations: [],
selectedServices: [],
selectedPoints: [],
mapCenter: [lng, lat],
locationGeoCoords: [polydata]
});
} else {
const data = this.state[selectedType];
if (data.indexOf(id) === -1) {
//push id
updatedData = [...data, id];
} else {
updatedData = data.filter((value) => value !== id);
}
}
if(type) {
this.setState({
[selectedType]: updatedData,
refreshData: true
});
}
};
render() {
return (
<component
selectedCountries={this.state.selectedCountries}
selectedLocations={this.state.selectedLocations}
refreshData={this.state.refreshData}
/>
);
}
}
did you make sure that the props of locations & countries are actually changing? If yes, the following code should work:
componentDidUpdate(prevProps) {
if (this.props.selectedCountries.length !== prevProps.selectedCountries.length) {
console.log("updated selected countries");
}
if (this.props.selectedLocations.length !== prevProps.selectedLocations.length) {
console.log("updated selected locations");
}
}
I created a fiddle for showcasing the effect here:
https://codesandbox.io/s/o580n8lnv5
I ran into this very issue. My solution was to send downstream to child components a clone of the state in question. This way when the state changes in App.js again, it will not affect the copy of the state passed down to children since those children were given a clone. In the previous props passed to async componentDidUpdate (prevProps) in child components, prevProps will be the clone that was originally handed down, and current props will be the most recent state changes made in App.js, which again is a clone, but prev and current props will be different.
Below is snipppet from App.render(), notice the value assign to the filter attribute, namely a clone the portion of the state in question:
...
<Routes onCategorySelect={this.handleCategorySelect}
onCategoryUnselect={this.handleCategoryUnselect}
onRouteLoad={this.handleRouteLoad} filter={this.cloneFilter(savedState.filter)}
updatedDimension={this.state.updatedDimension}/>
...
And this is the componentDidUpdate() of the child component:
async componentDidUpdate (prevProps) {
if (this.props.filter !== prevProps.filter && this.props.updatedDimension !== this.dimension) {
await this.updateChart()
}
}

Filter out existing objects in an array of objects

I don't think this is difficult, I just can't figure out the best way to do it. This function is creating an array, from a group of checkboxes. I then want to break up the array and create an array of objects, because each object can have corresponding data. How do I filter out existing rolesInterestedIn.roleType.
handleTypeOfWorkSelection(event) {
const newSelection = event.target.value;
let newSelectionArray;
if(this.state.typeOfWork.indexOf(newSelection) > -1) {
newSelectionArray = this.state.typeOfWork.filter(s => s !== newSelection)
} else {
newSelectionArray = [...this.state.typeOfWork, newSelection];
}
this.setState({ typeOfWork: newSelectionArray }, function() {
this.state.typeOfWork.map((type) => {
this.setState({
rolesInterestedIn: this.state.rolesInterestedIn.concat([
{
roleType: type,
}
])
}, function() {
console.log(this.state.rolesInterestedIn);
});
})
});
}
UDPATE
rolesInterestedIn: [
{
roleType: '',
experienceYears: ''
}
],
Because each time you do setState you are concatenating the new value to the prev one in rolesInterestedIn array. Add new value only when you are adding new item, otherwise remove the object from both the state variable typeOfWork and rolesInterestedIn.
Try this:
handleTypeOfWorkSelection(event) {
const newSelection = event.target.value;
let newSelectionArray, rolesInterestedIn = this.state.rolesInterestedIn.slice(0);
if(this.state.typeOfWork.indexOf(newSelection) > -1) {
newSelectionArray = this.state.typeOfWork.filter(s => s !== newSelection);
rolesInterestedIn = rolesInterestedIn.filter(s => s.roleType !== newSelection)
} else {
newSelectionArray = [...this.state.typeOfWork, newSelection];
rolesInterestedIn = newSelectionArray.map((workType) => {
return {
roleType: workType,
experienceYears: '',
}
});
}
this.setState({
typeOfWork: newSelectionArray,
rolesInterestedIn: rolesInterestedIn
});
}
Suggestion: Don't use multiple setState within a function, do all the calculation then use setState once to update all the values in the last.

Resources