I have an object defined like this:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {
lists: ["Dogs", "Cats"],
items: {Dogs:[], Cats:[]}
};
}
handleAddItem(item) {
console.log(this.props.idName);
console.log(item);
}
I have the variable
console.log(this.props.idName)// output: Dogs
console.log(item);// output {name: "lofi"}
I don't know how to update the object items{} to make it becоme like this:
items{Dogs:[{name: "lofi"}], Cats:[]}
To update a nested Array substate, you can use the spread operator to append elements
handleAddItem = item => {
this.setState((prevState, props) => ({
items: {
...prevState.items,
[props.idName]: [
...prevState.items[props.idName],
item
]
}
}))
}
What about something like that
handleAddItem(item) {
this.setState((s, p) => ({
items: {
...s.items,
[p.idName]: s.items[p.idName].concat([item])
}
}))
}
Few comments:
setState can take function, as parameter you got old state value
[idName]: value dynamically updates prop idName
You can do something like
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {
lists: ["Dogs", "Cats"],
items: {Dogs:[], Cats:[]}
};
}
handleAddItem(item) {
console.log(this.props.idName);
console.log(item);
let oldItems = this.state.items;
oldItems[this.props.idName].push(item);
// Update new state for items
this.setState({
items: {...this.state.items}
})
}
Related
I have 1 empty array in react state for storing different images and videos. Like,
this.state = {
imageArray: []
}
Now I am getting all the images and videos from my redux in an array. that array would be like,
fetchDataFromRedux:[{key:01_image, value: 'https://....},{key:02_image, value: 'https://....},{key:01_video, value: 'https://....}]
Now I want to append fetchDataFromRedux array into this.state.imageArray.
Currently, I am doing like this in componentDidUpdate while prevProps and newProps are not equal,
this.setState({imageArray: [...this.state.imageArray, ...fetchDataFromRedux]})
But whenever a new image or video added the length of the array would be double.
Does setting state with prevState value work as you intend?
this.setState((prevState) => {imageArray: [...prevState.imageArray, ...fetchDataFromRedux]})
We can use the Map to remove the duplicates when you update the state in componentDidUpdate.
Note: I am removing the duplicates when updating the state in componentDidMount. You can do the same on componentDidUpdate
import React from "react";
import ReactDOM from "react-dom";
import { Grid, Row, Col } from "react-flexbox-grid";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
imageArray: [
{
key: "01_image",
value: "https://...."
},
{
key: "02_image",
value: "https://...."
}
]
};
}
componentDidMount() {
const { imageArray } = this.state;
const updatedArr = [
{
key: "02_image",
value: "https://...."
},
{
key: "03_image",
value: "https://...."
}
];
const mergeArr = imageArray.concat(updatedArr);
const mapArr = new Map(mergeArr.map((item) => [item.key, item]));
this.setState({
imageArray: [...mapArr.values()]
});
}
render() {
const { imageArray } = this.state;
return (
<Grid>
<Row>
{imageArray.map((item) => (
<Col>{item.key}</Col>
))}
</Row>
</Grid>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
Working Code: https://codesandbox.io/s/react-playground-forked-vxb0s?file=/index.js
Happy coding !!!
Here I am calling the Function openProblem every time and want to re-render the list component with different prop arguments.
I have this.state and function calling but it didn't worked out.
import React, {Component} from 'react';
import List from './demoList'
import './userHome.css'
import ReactDOM from 'react-dom';
class UserHome extends Component{
constructor(props){
super(props);
this.state = {
list: [],
}
}
openProblem = (e) =>{
console.log("difficulty = "+e.target.value)
if(e.target.value==="Easy") {
this.state.list = [1,2,3,4,5]
} else if(e.target.value==="Medium") {
this.state.list = [6,7,8,9,10]
} else {
this.state.list = [11,12,13,14,15]
}
console.log("list = "+this.state.list)
ReactDOM.render(<List list = {this.state.list}/>, document.getElementById('walla'));
}
This is the List Component which I Want to render:
import React from 'react';
import ReactDOM from 'react-dom';
class List extends React.Component {
constructor(props){
super(props);
this.state = {
list: this.props.list
}
// this.ShowProblemContent = this.ShowProblemContent.bind(this);
console.log("inside demoList = ");
}
ShowProblemContent = (e) => {
ReactDOM.render((e.target.value), document.getElementById('showProblemContent'));
}
render(){
return(
<ul>
{this.state.list.map(item => (
<button value = {item} onClick = {this.ShowProblemContent} key={item}>{item}</button>
))}
</ul>
// <button>{this.state.list}</button>
);
}
}
I dont know where in your code you are calling openProblem, but in any case, React's state is immutable. You should only change the state using setState, which means openProblem should look as follows:
openProblem = e => {
console.log("difficulty = "+e.target.value)
if(e.target.value==="Easy") {
this.setState({ list: [1,2,3,4,5] })
} else if(e.target.value==="Medium") {
this.setState({ list: [6,7,8,9,10] })
} else {
this.setState({ list: [11,12,13,14,15] })
}
// setState is async, so this console.log might show outdated data.
console.log("list = "+this.state.list)
}
Read more about React's state here.
Also, I dont know why you are calling ReactDOM.render in the component. The only place where you should call this function is index.js, to render the entire app into #root. In classic React components, you just have to implement the render function, so your final component should look like this:
import React, {Component} from 'react';
import List from './demoList'
import './userHome.css'
import ReactDOM from 'react-dom';
class UserHome extends Component {
constructor(props){
super(props);
this.state = {
list: [],
}
}
openProblem = e => {
console.log("difficulty = "+e.target.value)
if(e.target.value==="Easy") {
this.setState({ list: [1,2,3,4,5] })
} else if(e.target.value==="Medium") {
this.setState({ list: [6,7,8,9,10] })
} else {
this.setState({ list: [11,12,13,14,15] })
}
// setState is async, so this console.log might show outdated data.
console.log("list = "+this.state.list)
}
render() {
return <List list={this.state.list} />
}
}
I am running into a strange issue where a component is updating a variable in the parent component that was passed to it as a prop.
The structure looks vaguely like so:
class ParentComponent extends Component {
const toPassToChild = [{ name: 'name', val: 0 }];
...
render() {
return(<ChildComponent p={toPassToChild} />);
}
}
class ChildComponent extends Component {
constructor(props) {
this.state = {
...
arrayOfObjects: this.props.p
}
}
modifyState() {
let aCopy = [...this.state.arrayOfObjects];
let member = aCopy.find(element => {
return element.name === 'name';
});
member.name = 'foo';
this.setState({
arrayOfObjects: aCopy
)};
}
}
When modifyState() is called, the value of toPassToChild is changed in ParentComponent to [{name: 'foo', val: 0}]. Is there any way to stop this? The issue does not occur with other props that are used as initial state, only the prop which is an array of objects.
When you modify member.name in modifyState, you're mutating the original object, since [...this.state.arrayOfObjects] still contains the references to the original objects.
Here's how you can update the array without mutating the original:
modifyState() {
const arrayOfObjects = this.state.arrayOfObjects.map(obj => {
if (obj.name === 'name') {
return { ...obj, name: 'foo' };
}
return obj;
});
this.setState({ arrayOfObjects });
}
I have change the way you copy in the child component. [...] only makes copy of first level elements.
class ParentComponent extends Component {
const toPassToChild = [{ name: 'name', val: 0 }];
...
render() {
return(<ChildComponent p={toPassToChild} />);
}
}
class ChildComponent extends Component {
constructor(props) {
this.state = {
...
arrayOfObjects: this.props.p
}
}
modifyState() {
let aCopy = JSON.parse(JSON.stringify(this.state.arrayOfObjects));
let member = aCopy.find(element => {
return element.name === 'name';
});
member.name = 'foo';
this.setState({
arrayOfObjects: aCopy
)};
}
}
Check the link for working version https://stackblitz.com/edit/react-kxtvoi?file=index.js
I have an object defined like this:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {
lists: ["Dogs", "Cats"],
items: {Dogs:[], Cats:[]}
};
}
handleAddItem(item) {
console.log(item);
}
I have the variable
console.log(item);// output {Dogs:[{name: "lofi"}]}
I don't know how to verify which property is in the item (Dogs or Cats) so that I can update the object items{} to make it becоme in my example like this:
items{Dogs:[{name: "lofi"}], Cats:[]}
You can use Object.keys method.
items[Object.keys(item)[0]] = item[Object.keys(item)[0]]
Working solution
let state = {
lists: ["Dogs", "Cats"],
items: {Dogs:[], Cats:[]}
};
let item = { Dogs:[{name: "lofi"}] };
state.items[Object.keys(item)[0]] = item[Object.keys(item)[0]]
console.log(state.items);
You can use Object.assign function on the state.items, which will add/update the given object with the properties given at the second parameter.
let state = {
lists: ["Dogs", "Cats"],
items: { Dogs:[], Cats:[] }
};
let item = { Dogs: [ {name: "lofi"} ] };
Object.assign(state.items, item);
console.log(state);
In my react component I do get some data from graphQL server using react-apollo.
That's working fine, but this component is a child component and I need to get the graphQL data to a parent component. Is this possible at all or do I have to change my structure?
Child
export class Child extends Component {
const { contentList } = this.props
render () {
contentList.map(elm => return <div>elm</div>)
}
}
export default compose(
graphql(
gql`
query {
contentList {
_id
title
}
}
`, { name: 'contentList' }
)
)(Child)
Parent
export class Parent extends Component {
constructor () {
super()
this.state = {
data = null // <-- Need to get the data of child here
}
}
render () {
const { data } = this.state
// Now I can use the data, which is fetched by the child component
return <Child />
}
}
You can pass a function of parent to the childComponent via props and invoke that function from child when you have data.
export class Parent extends Component {
constructor () {
super();
this.handleData = this.handleData.bind(this);
this.state = {
data = null // <-- Need to get the data of child here
}
}
handleData(data){
//you can use data here
}
render () {
const { data } = this.state
// Now I can use the data, which is fetched by the child component
return <Child parentHandler="this.handleData" />
}
}
export class Child extends Component {
const { contentList, parentHandler } = this.props;
//call parentHandler with the data when you have data
render () {
contentList.map(elm => return <div>elm</div>)
}
}
export default compose(
graphql(
gql`
query {
contentList {
_id
title
}
}
`, { name: 'contentList' }
)
)(Child)