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);
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 !!!
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 want to keep some functions outside of my component for easier testing. However, I cannot change state with these functions because they cannot reference the component's state directly.
So I currently have the hacky solution where I set the function to a variable then call this.setState. Is there a better convention/more efficient way to do this?
Example function code in Tester.js:
const tester = () => {
return 'new data';
}
export default tester;
Example component code in App.js (without imports):
class App extends Component {
constructor() {
super();
this.state = {
data: ''
}
}
componentDidMount(){
let newData = tester();
this.setState({ data: newData })
}
render() {
return(
<div>{this.state.data}</div>
)
}
}
You could bind your tester function like this (this approach doesn't work with arrow functions):
function tester() {
this.setState({ data: 'new Data' });
}
class App extends Component {
constructor() {
super();
this.state = {
data: '',
};
this.tester = tester.bind(this);
}
componentDidMount() {
this.tester();
}
render() {
return (
<div>{this.state.data}</div>
);
}
}
But I would prefer a cleaner approach, where you don't need your function to access this (also works with arrow functions):
function tester(prevState, props) {
return {
...prevState,
data: 'new Data',
};
}
class App extends Component {
constructor() {
super();
this.state = {
data: '',
};
}
componentDidMount() {
this.setState(tester);
}
render() {
return (
<div>{this.state.data}</div>
);
}
}
You can pass a function to setState() that will return a new object representing the new state of your component. So you could do this:
const tester = (previousState, props) => {
return {
...previousState,
data: 'new data',
};
}
class App extends Component {
constructor() {
super();
this.state = {
data: ''
}
}
componentDidMount(){
this.setState(tester)
}
render() {
return(
<div>{this.state.data}</div>
)
}
}
The reason being that you now have access to your component's previous state and props in your tester function.
If you just need access to unchanging static placeholder values inside of your app, for example Lorem Ipsum or something else, then just export your data as a JSON object and use it like that:
// testData.js
export const testData = {
foo: "bar",
baz: 7,
};
...
// In your app.jsx file
import testData from "./testData.js";
const qux = testData.foo; // "bar"
etc.
I am using vuex-typescript. This is one store module:
import { getStoreAccessors } from "vuex-typescript";
import Vue from "vue";
import store from "../../store";
import { ActionContext } from "vuex";
class State {
history: Array<object>;
}
const state: State = {
history: [],
};
export const history_ = {
namespaced: true,
getters: {
history: (state: State) => {
return state.history;
},
},
mutations: {
addToHistory (state: State, someob: any) {
state.history.push(someob);
},
resetState: (s: State) => {
const initial = state;
Object.keys(initial).forEach(key => { s[key] = initial[key]; });
},
},
actions: {
addToHistory(context: ActionContext<State, any>, someob: any) {
commitAddToHistory(store, someob);
}
}
const { commit, read, dispatch } =
getStoreAccessors<State, any>("history_");
const mutations = history_.mutations;
const getters = history_.getters;
const actions = history_.actions;
export const commitResetState = commit(mutations.resetState);
export const commitAddToHistory = commit(mutations.addToHistory);
export const getHistory = read(getters.history);
export const dispatchAddToSearchHistory = dispatch(actions.addToHistory);
Now if if call dispatchAddToSearchHistory or commitAddToHistory it is always the same all values get overwritten. For example if I add one element to store then it looks like this:
store = [
{
a: 1
}
]
now when I add another object {b: 2} store becomes
store = [
{
b: 2
},
{
b: 2
}
]
All values are owerwritten by last entry. For example if I try to add {c:3} then store looks like (and so on):
store = [
{
c: 3
},
{
c: 3
},
{
c: 3
}
]
....hmmmm, well, I think you may be sending the same object, every time
please try this mutation
addToHistory (state: State, someob: any) {
state.history.push({...someob});
},
or this action
addToHistory(context: ActionContext<State, any>, someob: any) {
commitAddToHistory(store, {...someob});
}
this, using the spread operator, clones the object. That way every item you add will be new object.
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}
})
}