I want to update state array object by particular id.
Suppose I have following object in state. And I tried to update by following way using id but, it doesn't work for me.
It didn't update state data.
this.state = {
data: [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
}
publishCurrentProject = user => {
this.setState(prevState => ({
data: prevState.data.map(item =>
item.id === user.id ? { ...user } : item
),
}))
}
let user = {id:'124',name:'ttt'};
publishCurrentProject(user);
Any help would be greatly appreciated.
Maybe the problem is on how you called the publishCurrentProject(), maybe you put that function in the wrong context. I use your implementation and it still works
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{ id: "124", name: "qqq" },
{ id: "589", name: "www" },
{ id: "45", name: "eee" },
{ id: "567", name: "rrr" }
]
};
this.handleClick = this.handleClick.bind(this);
this.publishCurrentProject = this.publishCurrentProject.bind(this);
}
handleClick(e) {
let user = { id: "124", name: "ttt" };
this.publishCurrentProject(user);
}
publishCurrentProject(user) {
this.setState((prevState) => ({
data: prevState.data.map((item) =>
item.id === user.id ? { ...user } : item
)
}));
}
render() {
return (
<div className="App">
<h1>Test</h1>
{this.state.data.map((el) => (
<p>{el.name}</p>
))}
<button onClick={this.handleClick}>Change</button>
</div>
);
}
}
Codesandbox for worked example
Related
So this has me puzzled. I've been banging my head against the wall trying to figure this out.
So I am trying to remove an object from a state managed array. I don't believe I am mutating the array.
I am using prevState. My delete function which gets sent to another component
{this.state.educationArray.map((item, i) => (<RenderEducation education={item} onDelete={this.handleDelete} />))}
Sending back the id to the handleDelete function.
My handleDelete:
handleDelete = itemId => {
//const newStudy = this.state.educationArray.filter(item => { return item.id !== itemId });
//this.setState({ educationArray: newStudy })
let tempArray = [];
let num = this.state.educationArray.length;
for (let i = 0; i < num;) {
//console.log("itemId is: ", itemId)
let tempId = this.state.educationArray[i].id
if (tempId != itemId) {
let obj = this.state.educationArray[i]
tempArray.push(obj)
}
i++
}
this.setState(prevState => ({ educationArray: tempArray }));
}
Stack Snippet w/loop:
const { useState } = React;
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
educationArray: [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
],
};
}
handleDelete = (itemId) => {
// const newStudy = this.state.educationArray.filter(item => { return item.id !== itemId });
// this.setState({ educationArray: newStudy })
let tempArray = [];
let num = this.state.educationArray.length;
for (let i = 0; i < num; ) {
//console.log("itemId is: ", itemId)
let tempId = this.state.educationArray[i].id;
if (tempId != itemId) {
let obj = this.state.educationArray[i];
tempArray.push(obj);
}
i++;
}
this.setState((prevState) => ({ educationArray: tempArray }));
};
render() {
return (
<ul>
{this.state.educationArray.map((element) => (
<li key={element.id}>
{element.name}{" "}
<input type="button" value="Del" onClick={() => this.handleDelete(element.id)} />
</li>
))}
</ul>
);
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
Stack Snippet w/filter:
const { useState } = React;
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
educationArray: [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
],
};
}
handleDelete = (itemId) => {
const newStudy = this.state.educationArray.filter(item => { return item.id !== itemId });
this.setState({ educationArray: newStudy })
/*
let tempArray = [];
let num = this.state.educationArray.length;
for (let i = 0; i < num; ) {
//console.log("itemId is: ", itemId)
let tempId = this.state.educationArray[i].id;
if (tempId != itemId) {
let obj = this.state.educationArray[i];
tempArray.push(obj);
}
i++;
}
this.setState((prevState) => ({ educationArray: tempArray }));
*/
};
render() {
return (
<ul>
{this.state.educationArray.map((element) => (
<li key={element.id}>
{element.name}{" "}
<input type="button" value="Del" onClick={() => this.handleDelete(element.id)} />
</li>
))}
</ul>
);
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
I've tried using the 2 lines commented out, I've tried rearranging how I do the for loop, its always the same result, it never removes the intended id.
I have sent console.log after console.log of the ids getting moved around and every seems to be working, but when it comes right now to push the specific objects that don't match the id to the temp array it never works and the object add the end gets removed.
Please and thank you for your advice
EDIT:
i call the handleDelete inside RenderEducation component:
<button onClick={() => this.props.onDelete(this.state.id)}> X - {this.state.id}</button>
from each
and my constructor:
constructor(props) {
super(props);
this.state = {
educationArray: [],
}
}
and how i add to the array:
addEducation = (e) => {
e.preventDefault();
this.setState(prevState => ({
educationArray: [...prevState.educationArray, {
id: uniqid(),
school: '',
study: '',
dateFrom: '',
dateTo: '',
editing: true,
}]
}))
}
Both versions of your code work in regular, non-edge-case situations, as we can see from the Stack Snippets I added to your question. The only problem I can see with the code shown is that it's using a potentially out-of-date version of the educationArray. Whenever you're updating state based on existing state, it's best to use the callback form of the state setter and use the up-to-date state information provided to the callback. Both of your versions (even your loop version, which does use the callback) are using this.state.educationArray instead, which could be out of date.
Instead, use the array in the state passed to the callback:
handleDelete = (itemId) => {
// Work with up-to-date state via the callback
this.setState(({educationArray: prevArray}) => {
// Filter out the element you're removing
return {
educationArray: prevArray.filter(({id}) => id !== itemId)
};
});
};
Live Example:
const { useState } = React;
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
educationArray: [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
],
};
}
handleDelete = (itemId) => {
// Work with up-to-date state via the callback
this.setState(({educationArray: prevArray}) => {
// Filter out the element you're removing
return {
educationArray: prevArray.filter(({id}) => id !== itemId)
};
});
};
render() {
return (
<ul>
{this.state.educationArray.map((element) => (
<li key={element.id}>
{element.name}{" "}
<input type="button" value="Del" onClick={() => this.handleDelete(element.id)} />
</li>
))}
</ul>
);
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
I'm trying to pass value from one component to another but when I do that, I get route in my http address as undefined instead of value. I have response from server in this form:
I'm trying to pass id values, and based on them make some actions. I get the error that GET request cannot be done due to value undefined.
Here is my code:
class StationService {
getStationById(id) {
return axios.get(STATION_API + '/station/' + id);
}
updateStation(station, id) {
return axios.put(STATION_API + '/station/' + id, station);
}
}
import React, { Component } from 'react';
import StationService from '../services/StationService';
class CreateStationComponent extends Component {
constructor(props) {
super(props)
this.state = {
station: {
id: this.props.match.params.id,
city: '',
name: '',
trains: [
{
number: '',
numberOfCarriages: ''
}
]
}
}
}
componentDidMount() {
if (this.state.station.id === '_add') {
return;
} else {
StationService.getStationById(this.state.id).then((res) => {
let station = res.data;
this.setState({ name: this.state.station[0].name, city: station[0].city })
});
}
console.log(this.state.station.name + 'dfddddd');
}
saveStation = (e) => {
e.preventDefault();
let station = { city: this.state[0].city, name: this.state[0].name }
if (this.state.id === '_add') {
StationService.createStation(station).then(res => {
this.props.history.push('/stations');
});
}
}
}
}
render() {
return (
<div>
...
</div >
);
}
}
From this component I want to pass id value to CreateStationComponent. But I don't know what I'm doing wrong.
import React, { Component } from 'react';
import StationService from '../services/StationService';
class ListStation extends Component {
constructor(props) {
super(props)
this.state = {
stations: []
}
this.addStation = this.addStation.bind(this);
this.editStation = this.editStation.bind(this);
this.deleteStation = this.deleteStation.bind(this);
this.showTrains = this.showTrains.bind(this);
}
deleteStation(id) {
StationService.deleteStation(id).then(res => {
this.setState({ stations: this.state.stations.filter(station => station.id !== id) });
})
}
editStation(id) {
this.props.history.push(`/add-station/${id}`);
}
componentDidMount() {
StationService.getStations().then((res) => {
this.setState({ stations: res.data });
})
}
render() {
return (
<div>
</div>
<div className="row">
<tbody>
{this.state.stations.map(
station =>
<tr key={station.id}>
<td>{station.city}</td>
<td>{station.name}</td>
<td>
<button onClick={() => this.editStation(station.id)} className="btn btn-info">Modify</button>
</tr>
)}
</tbody>
</table>
</div>
</div>
);
}
}
Any help would be appreciated.
Inside the constructor this.prop doesn't exist yet. Just access props.
constructor(props) {
super(props)
this.state = {
station: {
id: props.match.params.id,
city: '',
name: '',
trains: [
{
number: '',
numberOfCarriages: ''
}
]
}
}
}
Also pointed out in a comment, this.state.id isn't defined
StationService.getStationById(this.state.id)
but this.state.station.id is. Change the reference.
StationService.getStationById(this.state.station.id)
Since this.state.station is an object and not an array, this.setState({ name: this.state.station[0].name, city: station[0].city }) is also incorrect. this.state.station[0] is undefined and should throw error when attempting to access name. Update the reference.
this.setState({
name: this.state.station.name,
city: station[0].city,
})
And same for saveStation, update the state references.
saveStation = (e) => {
e.preventDefault();
let station = {
city: this.state.station.city,
name: this.state.station.name }
if (this.state.station.id === '_add') {
StationService.createStation(station).then(res => {
this.props.history.push('/stations');
});
}
}
I need to increase or decrease state value in catalog > spec > units, if I click on increase button the number in units should increase by one and if I click on decrease button it should decrease by one, I'd tried by setting state in the render, but it didn't work and I think this is not a good practice. How can I create a function to setState of units without declaring it inside the render method?
Here is an example of my code:
export default class Order extends Component {
constructor(props) {
super(props);
this.state = {
catalog: [
{
photo: 'https://via.placeholder.com/400x400',
title: 'My title',
description: 'Bla bla bla...',
spec: { size: 'FAM', units: 1, price: 999999, id: 'CMB0', selectedIndicator: '', isSelected: false, name: 'A simple name' },
isCombo: true
},
],
}
}
}
render(){
return(
{this.state.catalog.map((item, index) => {
<div key={index}>
<strong>{item.title}</strong>
<span>{item.spec.units}</span>
<button onClick={() => item.spec.units + 1}>increase</button>
<button onClick={() => item.spec.units - 1}>decrease</button>
</div>})
}
)
}
Try this
increase = title => {
const newCatalogState = this.state.catalog.map(item => {
if (item.title === title) {
return {
...item,
spec: {
...item.spec,
units: item.spec.units + 1
}
};
}
return item;
});
this.setState({
catalog: newCatalogState
});
};
decrease = title => {
const newCatalogState = this.state.catalog.map(item => {
if (item.title === title) {
return {
...item,
spec: {
...item.spec,
units: item.spec.units - 1
}
};
}
return item;
});
this.setState({
catalog: newCatalogState
});
};
<button onClick={() => this.increase(item.title)}>increase</button>
<button onClick={() => this.decrease(item.title)}>decrease</button>
you can check here codesandbox hope it helps
Try this:
export default class Order extends Component {
constructor(props) {
super(props);
this.state = {
catalog: [
{
photo: 'https://via.placeholder.com/400x400',
title: 'My title',
description: 'Bla bla bla...',
spec: { size: 'FAM', units: 1, price: 999999, id: 'CMB0', selectedIndicator: '', isSelected: false, name: 'A simple name' },
isCombo: true
},
],
}
}
}
const updateUnits = (index, value) => {
const { catalog } = this.state
catalog[index].spec.units += value
this.setState({catalog})
}
render(){
return(
{ this.state.catalog.map((item, index) => {
<div key={index}>
<strong>{item.title}</strong>
<span>{item.spec.units}</span>
<button onClick={() => this.updateUnits(index, 1)}>increase</button>
<button onClick={() => this.updateUnits(index, -1)}>decrease</button>
</div>})
}
)
}
I have a state object that contains an array of objects:
this.state = {
feeling: [
{ name: 'alert', status: false },
{ name: 'calm', status: false },
{ name: 'creative', status: false },
{ name: 'productive', status: false },
{ name: 'relaxed', status: false },
{ name: 'sleepy', status: false },
{ name: 'uplifted', status: false }
]
}
I want to toggle the boolean status from true to false on click event. I built this function as a click handler but it doesn't connect the event into the state change:
buttonToggle = (event) => {
event.persist();
const value = !event.target.value
this.setState( prevState => ({
status: !prevState.status
}))
}
I'm having a hard time following the control flow of the nested React state change, and how the active event makes the jump from the handler to the state object and vice versa.
The whole component:
export default class StatePractice extends React.Component {
constructor() {
super();
this.state = {
feeling: [
{ name: 'alert', status: false },
{ name: 'calm', status: false },
{ name: 'creative', status: false },
{ name: 'productive', status: false },
{ name: 'relaxed', status: false },
{ name: 'sleepy', status: false },
{ name: 'uplifted', status: false }
]
}
}
buttonToggle = (event) => {
event.persist();
const value = !event.target.value
this.setState( prevState => ({
status: !prevState.status
}))
}
render() {
return (
<div>
{ this.state.feeling.map(
(stateObj, index) => {
return <button
key={ index }
onClick={ this.buttonToggle }
value={ stateObj.status } >
{ stateObj.status.toString() }
</button>
}
)
}
</div>
)
}
}
In order to solve your problem, you should first send the index of the element that is going to be modified to your toggle function :
onClick = {this.buttonToggle(index)}
Then tweak the function to receive both the index and the event.
Now, to modify your state array, copy it, change the value you are looking for, and put it back in your state :
buttonToggle = index => event => {
event.persist();
const feeling = [...this.state.feeling]; //Copy your array
feeling[index] = !feeling[index];
this.setState({ feeling });
}
You can also use slice to copy your array, or even directly send a mapped array where only one value is changed.
Updating a nested object in a react state object is tricky. You have to get the entire object from the state in a temporary variable, update the value within that variable and then replace the state with the updated variable.
To do that, your buttonToggle function needs to know which button was pressed.
return <button
key={ index }
onClick={ (event) => this.buttonToggle(event, stateObj.name) }
value={ stateObj.status } >
{ stateObj.status.toString() }
</button>
And your buttonToggle function could look like this
buttonToggle = (event, name) => {
event.persist();
let { feeling } = this.state;
let newFeeling = [];
for (let index in feeling) {
let feel = feeling[index];
if (feel.name == name) {
feel = {name: feel.name, status: !feel.status};
}
newFeeling.push(feel);
}
this.setState({
feeling: newFeeling,
});
}
Here's a working JSFiddle.
Alternatively, if you don't need to store any more data per feeling than "name" and "status", you could rewrite your component state like this:
feeling: {
alert: false,
calm: false,
creative: false,
etc...
}
And buttonToggle:
buttonToggle = (event, name) => {
event.persist();
let { feeling } = this.state;
feeling[name] = !feeling[name];
this.setState({
feeling
});
}
I think you need to update the whole array when get the event. And it is better to not mutate the existing state. I would recommend the following code
export default class StatePractice extends React.Component {
constructor() {
super();
this.state = {
feeling: [
{ name: "alert", status: false },
{ name: "calm", status: false },
{ name: "creative", status: false },
{ name: "productive", status: false },
{ name: "relaxed", status: false },
{ name: "sleepy", status: false },
{ name: "uplifted", status: false },
],
};
}
buttonToggle = (index, value) => (event) => {
event.persist();
const toUpdate = { ...this.state.feeling[index], status: !value };
const feeling = [...this.state.feeling];
feeling.splice(index, 1, toUpdate);
this.setState({
feeling,
});
};
render() {
return (
<div>
{this.state.feeling.map((stateObj, index) => {
return (
<button
key={index}
onClick={this.buttonToggle(index, stateObj.status)}
value={stateObj.status}
>
{stateObj.status.toString()}
</button>
);
})}
</div>
);
}
}
I have some problem and am getting furios a little. I want to .map my Array of Objects. And make everyone of them clickable. After click I want the particular object to show only its Avatar:
const stations = [
{
name: 'first',
avatar: './img/1.jpg'
},
{
name: 'second',
avatar: './img/2.jpg'
},
{
name: 'third',
avatar: './img/3.jpg'
},
{
name: 'fourth',
avatar: './img/4.jpg'
},
{
name: 'fifth',
avatar: './img/5.jpg'
}
]
Right now. I can access the value I need from my Database Array. But! I have a problem with:
this.state = {
clicked: false
}
this.handleClick = this.handleClick.bind(this)
}
My objects do not have separate state. So when I want to create some action based on this.state (like hide and show) it always work on EVERY element.
I have code which works in some way. When I render the list and click on any button, action occurs for every of them:
class radiosList extends React.Component {
constructor() {
super();
this.state = {
clicked: false
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(station) {
console.log(this)
this.setState({ clicked: !this.state.clicked })
}
render () {
return (
<div>
{
this.props.stations.map((station, index) => (
<div className='radio_list radio_list--component' style={{cursor: 'pointer'}} onClick={() => this.handleClick(station.avatar)}>
<div className='radio_list radio_list--component radio_list--component-station_name'>{station.name}</div>
</div>
))
}
</div>
)
}
}
export default radiosList
Edit: first answer helped with accessing values I need.
This is the way you can achieve what you want by adding an additional clicked state attribute to data. There could be a better way but his is how I have done it for my purposes so far.
class radiosList extends React.Component {
constructor() {
super();
this.state = {
data: [],
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(index) {
console.log(this)
var data = [...this.state.data];
data[index].clicked = !data[index].clicked;
this.setState({data});
}
render () {
var self = this;
this.props.station.forEach(function(station) {
self.state.data.push({name: station.name, avatar: station.avatar, clicked: false});
self.setState({data: self.state.data});
})
return (
<div>
{
this.state.data.map((station, index) => (
<div className='radio_list radio_list--component' style={{cursor: 'pointer'}} onClick={() => this.handleClick(index)}>
<div className='radio_list radio_list--component radio_list--component-station_name'>{station.name}</div>
</div>
))
}
</div>
)
}
}
export default radiosList