change on of nested array object in react state - arrays

i want to replace the values of the nested array object like the below one, when button is clicked it will replace the old values of the x indexed object and set the new values there.
class compo extends React.Component {
constructor() {
super();
this.state = {
tabsData:[
{
id:1,
title:"OldTitle1"
},
{
id:2,
title:"OldTitle2"
}
],
}
this.changeTab = this.changeTab.bind(this)
}
changeTab(){
const newData={
id=3,
title="New One"
}
//replace the above new data in the second object of nested array in state
}
render(){
return(
<button type="button" >Add</button>
)
;}
}
export default compo
the state should be like this after
tabsData:[
{
id:1,
title:"OldTitle"
},
{
id:3,
title:"New One"
}
]

Not able to comment as my rep is less than 50...based on an idea of what you need here is the code.
https://codesandbox.io/s/brave-lumiere-dh9ry?file=/src/App.js
const [data, setData] = React.useState([
{
id: 1,
title: "OldTitle1"
},
{
id: 2,
title: "OldTitle2"
}
]);
const newData = { id: 3, title: "New One" };
const addData = () => {
const newArr = data;
newArr[1] = newData;
console.log("newArr>>>>", newArr);
setData([...newArr]);
};

You could do something like this...
import React from "react";
class compo extends React.Component {
constructor() {
super();
this.state = {
tabsData: [
{
id: 1,
title: "OldTitle1"
},
{
id: 2,
title: "OldTitle2"
}
]
};
this.changeTab = this.changeTab.bind(this);
}
changeTab() {
const newData = {
id: 3,
title: "New One"
};
// Make duplicate since you can't mutatue state
let newTabData = [...this.state.tabsData];
const id = 2; // id to be removed
// CASE 1: If you want to maintain order
const index = newTabData.findIndex((data) => data.id === id);
if (index > -1) {
// replace oldData with newData
newTabData.splice(index, 1, newData);
} else {
// simply add newData at last
newTabData.push(newData);
}
// CASE 2: If order doesn't matter
// // remove oldData
// newTabData = newTabData.filter((data) => data.id !== id);
// // add new data at last
// newTabData.push(newData);
// finally update the state irrespective of any case
this.setState({ tabsData: newTabData });
}
render() {
return (
<div>
<button type="button">
Add
</button>
<button type="button" onClick={this.changeTab}>
Change
</button>
<br />
{JSON.stringify(this.state, null, 2)}
</div>
);
}
}
export default compo;

Related

Reactjs, control a list input

I need to control a list of list of input like this:
import React from "react";
import "./styles.css";
const testData = [
{
name: "list 1",
content: [
{
id: 1,
value: " this is value 1 of list 1"
},
{
id: 2,
value: " this is value 2 of list 1"
},
{
id: 3,
value: " this is value 3 of list 1 "
}
]
},
{
name: "list 2",
content: [
{
id: 1,
value: " this is value 1 of list 2"
},
{
id: 2,
value: " this is value 2 of list 2"
},
{
id: 3,
value: " this is vainput'liste 3 of list 2"
}
]
}
];
class MyList extends React.Component {
constructor(props) {
super(props);
this.state = {
data: this.props.list
};
}
handleChange = (e, index) => {
const newData = this.state.data;
newData[index] = e.target.value;
this.setState({
data: newData
});
};
updateData = newData => {
this.setState({
data: newData
});
};
add = () => {
const newData = this.state.data;
newData.push({
id: newData.length + 1,
value: "new data"
});
this.setState({
data: newData
});
};
remove = (e, index) => {
const newData = this.state.data;
newData.splice(index, 1);
this.setState({
data: newData
});
};
save = (e, index) => {
//TODO
};
render() {
return (
<div className="App">
<button onClick={this.add}> Add </button>
<button onClick={this.save}> save </button>
<tr>input'list
{this.state.data.map((item, index) => (
<td>
<input
value={item.value}
onChange={e => this.handleChange(e, index)}
/>
title
<button onClick={e => this.remove(e, index)}> remove </button>
</td>
))}
</tr>
</div>
);
}
}input'list
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
title: testData[0].name,
data: testData[0].content
};
this.ref = React.createRef();
}
changeList = (e, index) => {
this.setState({
title: testData[index].name,
data: testData[index].content
});
this.ref.current.updateData(testData[index].content);
};
render() {
return (
<>
{testData.map((item, index) => (
<button onClick={e => this.changeList(e, index)}>
{" "}
{item.name}{" "}
</button>
))}
this is {this.state.title}
<MyList ref={this.ref} list={this.state.data}>
{" "}
</MyList>{" "}
</>
);
}
}
export default App;
I need some basic method like show list, save, add more input in list, remove ... And with my code, everything works fine. But I feel it's not clean since there's many ways to do 1 thing in React. Can someone read it and give me some feedback? There's 2 things I thought are not good, but I don't know how to improve:
function handleChange of component MyList: I have to calculator index of input to re-render.
I use A Ref to update and get Data from component MyList.

Change title in this.state

I have this.state with an array, and in array I want to change title, but it is not working. Help me please
Code:
this.state =
{
list: [{title: "1"}, {title: "2"}],
}
// changing title
this.setState({
this.state.list.title: "newTitle"
}],
try this
this.setState({
list: [{title: 'new title'}, { title: 'other title' }]
});
Example
class App extends React.Component {
state = { list: [{title: "1"}, {title: "2"}] };
updateTitle = (index) => {
var { list } = this.state;
list[index].title = `Updated title ${index}`;
this.setState({list});
}
updateTitles = () => {
var { list } = this.state;
list.forEach((item, k) => {
item.title = `Title ${k}`;
});
this.setState({list});
}
render(){
var { list } = this.state;
return (
<div>
<button onClick={this.updateTitles}>Update Title</button>
<ul>
{list.map((item, k) => (
<li onClick={() => this.updateTitle(k)} key={item.title}>{item.title}</li>
))}
</ul>
</div>
);
}
}
I can't get your question correctly. If you want to change value of title key of every child of list array just declare a function like this
function changeValue () {
let newList = this.state.list
newList.forEach(item => {
item.title = 'newTitle'
})
this.setState = ({
list: newList
})
}

ReactJS won't print an array in the order that it is stored?

I have an issue with my reactjs code where the render() function won't print an array in the order that it is stored in my state object.
Here's my code which is pretty simple:
import React from "react";
export default class DonationDetail extends React.Component {
constructor(props) {
super(props);
this.state = { content: [] };
}
componentDidMount() {
let state = this.state;
state.content.push({ food: "burger" });
state.content.push({ food: "pizza" });
state.content.push({ food: "tacos" });
this.setState(state);
}
addPaymentItem() {
const item = { food: "" };
let state = this.state;
state.content.unshift(item);
this.setState(state);
}
render() {
console.log(this.state);
let ui = (
<div>
<button type="button" onClick={() => this.addPaymentItem()}>
add to top
</button>
{this.state.content.map((item, key) => (
<input type="text" key={key} defaultValue={item.food} />
))}
</div>
);
return ui;
}
}
When you press the button add to top, a new item is placed to the front of the state.content array, which you can verify from the console.log(this.state) statement. But what's unusual is that the HTML that is generated does NOT add this new item to the top of the user interface output. Instead, another input field with the word taco is placed at the bottom of the list in the user interface.
Why won't ReactJS print my state.content array in the order that it is actually stored?
You can use the array index as key when the order of the elements in the array will not change, but when you add an element to the beginning of the array the order is changed.
You could add a unique id to all your foods and use that as key instead.
Example
class DonationDetail extends React.Component {
state = { content: [] };
componentDidMount() {
const content = [];
content.push({ id: 1, food: "burger" });
content.push({ id: 2, food: "pizza" });
content.push({ id: 3, food: "tacos" });
this.setState({ content });
}
addPaymentItem = () => {
const item = { id: Math.random(), food: "" };
this.setState(prevState => ({ content: [item, ...prevState.content] }));
};
handleChange = (event, index) => {
const { value } = event.target;
this.setState(prevState => {
const content = [...prevState.content];
content[index] = { ...content[index], food: value };
return { content };
});
};
render() {
return (
<div>
<button type="button" onClick={this.addPaymentItem}>
add to top
</button>
{this.state.content.map((item, index) => (
<input
type="text"
key={item.id}
value={item.food}
onChange={event => this.handleChange(event, index)}
/>
))}
</div>
);
}
}
ReactDOM.render(<DonationDetail />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Instead of:
componentDidMount() {
let state = this.state;
state.content.push({ food: "burger" });
state.content.push({ food: "pizza" });
state.content.push({ food: "tacos" });
this.setState(state);
}
Try
componentDidMount() {
this.setState(prevState => ({
content: [
...prevState.content,
{ food: "burger" },
{ food: "pizza" },
{ food: "tacos" },
]
}));
}
and
addPaymentItem() {
const item = { food: "" };
let state = this.state;
state.content.unshift(item);
this.setState(state);
}
to
addPaymentItem() {
this.setState(prevState => ({
content: [
{ food: "" },
...prevState.content,
]
}));
}

How to add an object in an array of objects? ReactJS?

My Project is, an array of objects where i get only the names and render on screen of form 3 in 3, with button next and previous change the names, and can to filter for letters.
I would want add a new value, typped on input and clicked in the button add.
My code button:
addItem = () => {
const inValue = {
id: 0,
name: this.state.input
}
this.setState({
filtered: this.state.filtered.concat(inValue),
currentPage: 0
})
}
I would want the value inserted in the filtered array.
My code all:
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
const peoples =[{id:0, name:"Jean"},
{id:1, name:"Jaha"},
{id:2, name:"Rido"},
{id:3, name:"Ja"},
{id:4, name:"Letia"},
{id:5, name:"Di"},
{id:6, name:"Dane"},
{id:7, name:"Tamy"},
{id:8, name:"Tass"},
{id:9, name:"Ts"},
{id:10, name:"Abu"},
{id:11, name:"Ab"}];
this.state = {
elementsPerPage:3,
currentPage:0,
peoples,
input: "",
filtered: peoples,
teste: '',
};
}
getValueInput = (evt) => {
const inputValue = evt.target.value;
this.setState({ input: inputValue });
this.filterNames(inputValue);
}
filterNames = (inputValue)=> {
const { peoples } = this.state;
this.setState({
filtered: peoples.filter(item =>
item.name.includes(inputValue)),
currentPage:0
});
const Oi = this.state.filtered.map(item=>item.name);
if(Oi.length<=0){
alert('Você está adicionando um nome')
}
console.log(Oi)
}
elementsOnScreen = () => {
const {elementsPerPage, currentPage, filtered} = this.state;
return filtered
.map((item) => <li key={item.id}> {item.name} <button onClick={() => this.remove(item.name)}> Delete </button> </li>)
.slice(currentPage*elementsPerPage, currentPage*elementsPerPage + elementsPerPage);
if(this.state.filtered.length < 1){
this.setState({currentPage: this.state.currentPage - 1})
}
}
remove = (id) => {
console.log(this.state.filtered.length)
if(this.state.filtered.length < 0){
this.setState({currentPange: this.state.currenPage - 1})
}
this.setState({filtered: this.state.filtered.filter(item => item.name !== id) })
}
nextPage = () => {
console.log(this.state.filtered)
const {elementsPerPage, currentPage, filtered} = this.state;
if ((currentPage+1) * elementsPerPage < filtered.length){
this.setState({ currentPage: this.state.currentPage + 1 });
}
}
previousPage = () => {
const { currentPage } = this.state;
if(currentPage - 1 >= 0){
this.setState({ currentPage: this.state.currentPage - 1 });
}
}
addItem = () =>{
const inValue = {id:0 ,name: this.state.input}
this.setState({filtered: this.state.filtered.concat(inValue), currentPage: 0})
}
render() {
return (
<div>
<button onClick={this.addItem}> Add </button>
<input type="text" onChange={ this.getValueInput }></input>
<button onClick={this.previousPage}> Previous </button>
<button onClick={this.nextPage}> Next </button>
<h3>Current Page: {this.state.currentPage}</h3>
<ul>Names: {this.elementsOnScreen()}</ul>
</div>
);
}
}
export default App;
You would have the array of objects contained within your state, then use setState
this.state = {
elementsPerPage:3,
currentPage:0,
peoples,
input: "",
filtered: peoples,
teste: '',
peoples: [
{id:0, name:"Jean"},
{id:1, name:"Jaha"},
{id:2, name:"Rido"},
{id:3, name:"Ja"},
{id:4, name:"Letia"},
{id:5, name:"Di"},
{id:6, name:"Dane"},
{id:7, name:"Tamy"},
{id:8, name:"Tass"},
{id:9, name:"Ts"},
{id:10, name:"Abu"},
{id:11, name:"Ab"}];
};
To update the peoples array, you would first need to create a copy of the peoples array, modify the copy, then use setState to update.
let { peoples } = this.state;
peoples.push({ id:12, name:"Jean"})
this.setState({peoples: peoples})
Looks like you are already updating your state with the typed input.
So in your add button you can get the state value and push it to your people array. Something like this:
addItem = () => {
const { inputValue, people } = this.state;
if (!inputValue) return; // check if inputValue has any value
people.push({ id: people.length+1, name: inputValue )} // I don't recommend using sequencial ids like this, you'd better have a handler to generate it for you
this.setState({ people, inputValue: '' }); // update people and clear input
}
Hope it helps

React apply function to single item in array

The following code creates a simple movie rating app. Everything works except that when an up or down vote is clicked in one of the array items, the votes state for all items in the array update, rather than just for the item that was clicked. How do I code this so that the vote only applies to the item where it was clicked?
class Ratings extends React.Component {
constructor(props){
super(props);
this.state = {
votes: 0
};
this.add = this.add.bind(this);
this.subtract = this.subtract.bind(this);
this.reset = this.reset.bind(this);
}
add(event){
this.setState ({
votes: this.state.votes + 1
})
}
subtract(event){
this.setState ({
votes: this.state.votes - 1
})
}
reset(event){
this.setState ({
votes: 0
})
}
render () {
this.movies = this.props.list.map(x => {
return (
<div key={x.id} className="movierater">
<MoviePoster poster={x.img}/>
<h1 className="title">{x.name}</h1>
<div className="votewrapper">
<button onClick={this.add}><i className="votebutton fa fa-thumbs-o-up" aria-hidden="true"></i></button>
<Votes count={this.state.votes} />
<button onClick={this.subtract}><i className="votebutton fa fa-thumbs-o-down" aria-hidden="true"></i></button>
</div>
<button onClick={this.reset} className="reset">Reset</button>
</div>
)
});
return (
<div>
{this.movies}
</div>
);
}
}
function MoviePoster(props) {
return (
<img src={props.poster} alt="Movie Poster" className="poster"/>
);
}
function Votes(props) {
return (
<h2>Votes: {props.count}</h2>
);
}
var movieposters = [
{id: 1, img:"http://www.impawards.com/2017/posters/med_alien_covenant_ver4.jpg", name: "Alien Covenant"},
{id: 2, img:"http://www.impawards.com/2017/posters/med_atomic_blonde_ver4.jpg", name: "Atomic Blonde"},
{id: 3, img:"http://www.impawards.com/2017/posters/med_easy_living_ver3.jpg", name: "Easy Living"},
{id: 4, img:"http://www.impawards.com/2017/posters/med_once_upon_a_time_in_venice_ver3.jpg", name: "Once Upon a Time in Venice"},
{id: 5, img:"http://www.impawards.com/2017/posters/med_scorched_earth.jpg", name: "Scorched Earth"},
{id: 6, img:"http://www.impawards.com/2017/posters/med_underworld_blood_wars_ver9.jpg", name: "Underworld: Blood Wars"},
{id: 7, img:"http://www.impawards.com/2017/posters/med_void.jpg", name: "The Void"},
{id: 8, img:"http://www.impawards.com/2017/posters/med_war_for_the_planet_of_the_apes.jpg", name: "War for the Planet of the Apes"},
]
ReactDOM.render(
<Ratings list={movieposters} />,
document.getElementById('app')
);
You need a separate vote count for each movie entity.
This can be accomplished by providing an id to each movie and setting the vote for that specific movie by id.
I would also recommend to extract a new component for a Movie.
this component will get the movieId as a prop and the handlers, it will invoke the up or down handlers and provide to them the current movieId.
See a running example:
class Movie extends React.Component {
onSubtract = () => {
const { subtract, movieId } = this.props;
subtract(movieId);
};
onAdd = () => {
const { add, movieId } = this.props;
add(movieId);
};
onReset = () => {
const { reset, movieId } = this.props;
reset(movieId);
};
render() {
const { movie, votes = 0 } = this.props;
return (
<div className="movierater">
<MoviePoster poster={movie.img} />
<h1 className="title">{movie.name}</h1>
<div className="votewrapper">
<button onClick={this.onAdd}>
<i className="votebutton fa fa-thumbs-o-up" aria-hidden="true" />
</button>
<Votes count={votes} />
<button onClick={this.onSubtract}>
<i className="votebutton fa fa-thumbs-o-down" aria-hidden="true" />
</button>
</div>
<button onClick={this.onReset} className="reset">
Reset
</button>
</div>
);
}
}
class Ratings extends React.Component {
constructor(props) {
super(props);
this.state = {
allVotes: {}
};
}
subtract = movieId => {
const { allVotes } = this.state;
const currentVote = allVotes[movieId] || 0;
const nextState = {
...allVotes,
[movieId]: currentVote - 1
};
this.setState({allVotes: nextState});
};
add = movieId => {
const { allVotes } = this.state;
const currentVote = allVotes[movieId] || 0;
const nextState = {
...allVotes,
[movieId]: currentVote + 1
};
this.setState({ allVotes: nextState });
};
reset = movieId => {
const { allVotes } = this.state;
const nextState = {
...allVotes,
[movieId]: 0
};
this.setState({ allVotes: nextState });
};
render() {
const { allVotes } = this.state;
this.movies = this.props.list.map(x => {
const votes = allVotes[x.id];
return (
<Movie
movieId={x.id}
movie={x}
votes={votes}
reset={this.reset}
subtract={this.subtract}
add={this.add}
/>
);
});
return <div>{this.movies}</div>;
}
}
function MoviePoster(props) {
return <img src={props.poster} alt="Movie Poster" className="poster" />;
}
function Votes(props) {
return <h2>Votes: {props.count}</h2>;
}
var movieposters = [
{
id: 1,
img: "http://www.impawards.com/2017/posters/med_alien_covenant_ver4.jpg",
name: "Alien Covenant"
},
{
id: 2,
img: "http://www.impawards.com/2017/posters/med_atomic_blonde_ver4.jpg",
name: "Atomic Blonde"
},
{
id: 3,
img: "http://www.impawards.com/2017/posters/med_easy_living_ver3.jpg",
name: "Easy Living"
},
{
id: 4,
img:
"http://www.impawards.com/2017/posters/med_once_upon_a_time_in_venice_ver3.jpg",
name: "Once Upon a Time in Venice"
},
{
id: 5,
img: "http://www.impawards.com/2017/posters/med_scorched_earth.jpg",
name: "Scorched Earth"
},
{
id: 6,
img:
"http://www.impawards.com/2017/posters/med_underworld_blood_wars_ver9.jpg",
name: "Underworld: Blood Wars"
},
{
id: 7,
img: "http://www.impawards.com/2017/posters/med_void.jpg",
name: "The Void"
},
{
id: 8,
img:
"http://www.impawards.com/2017/posters/med_war_for_the_planet_of_the_apes.jpg",
name: "War for the Planet of the Apes"
}
];
ReactDOM.render(<Ratings list={movieposters} />, document.getElementById("root"));
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
You need to keep track of votes for each element, thus this.state.votes +/- 1 doesn't do the job, so:
Change
this.state = {
votes: 0
}
to
this.state = {
votes: {}
}
then change the functions:
add(id){
return function(event) {
this.setState ({ ...this.state.votes, [id]: parseInt(this.state.votes[id]) + 1 })
}
}
and the same for subtract. Then change your buttons to:
<button onClick={this.add(x.id)} ... (same for subtract)
and last change your Vote component:
<Votes count={this.state.votes[x.id] || 0} />
On reset just do:
reset(event){
this.setState ({ votes: {} })
}

Resources