How to update existing object - reactjs

I'm trying to update current object within an array with new property using state hooks. The array with object looks like this:
const myData = [
{
dataLabels: [
{
align: 'left'
}
],
name: 'my data',
data: [
{
y: 1,
name: 'Daryl'
},
{
y: 2,
name: 'Negan'
}
]
}
];
and I wan't to add color property to data objects inside useState hook. This is what I've tried so far:
const [ newMyData ] = useState({
...myData,
0: {
...myData[0],
data: myData[0].data.map((item, index) => ({ ...item, color: getChartColors()[index] }))
},
});
but the problem is that newMyData is now turned into an object instead of keep being an array. What am I doing wrong and how should I solve my problem? Thanks in advance

You are passing an object as the initial state:
const [ newMyData ] = useState([ /* <--- use '[' not '{' */
...myData,
0: {
...myData[0],
data: myData[0].data.map((item, index) => ({ ...item, color: getChartColors()[index] }))
},
] /* <--- same here - use ']' not '}' */ );
UPDATE:
Based on what you asked in the comments:
const myData = [
{
dataLabels: [
{
align: 'left'
}
],
name: 'my data',
data: [
{
y: 1,
name: 'Daryl'
},
{
y: 2,
name: 'Negan'
}
]
}
];
const myObject = myData[0];
const nextObject = {
...myObject,
data: myObject.data.map((item, index) => ({ ...item, color: getChartColors()[index] }))
}
const [myData, setMyData] = useState([ nextObject ]); /* If you still want this to be an array */
/* OR */
const [myData, setMyData] = useState( nextObject ); /* If you want it to be an object instead */

Hi you can follow this example to include new property in array using useState hooks.
import React, {useState} from "react";
export default function UseStateExample() {
const [myData, setMyData] = useState([
{
dataLabels: [
{align: 'left'}
],
name: 'my data',
data: [
{y: 1, name: 'Daryl'},
{y: 2, name: 'Negan'}
]
}
]);
function getChartColors() {
return ["red", "green", "blue"]
}
function clickHandler(event) {
let items = [];
myData[0].data.map((item, index) => {
item.color = getChartColors()[index];
items.push(item);
});
setMyData([
...myData
]);
console.log(myData)
}
return (
<div>
<button onClick={clickHandler}>Update myData and show in Console</button>
</div>
);
}

Related

Why can't I push in <option> when I get the 'response.data'?

Why can't I push in my <option> when I get the response.data?
type State = {
companyManagerMap: null | Map<string, string[]>
}
useEffect(() => {
AdminListManager()
.then((response) => {
const { data } = response.data
console.log( { data });
setState((s) => ({
...s,
companyManagerMap: new Map(
Object.keys(data).map((key) => [key, data[key]])
),
}))
})
.catch(showUnexpectedError)
}, [showUnexpectedError])
data format
{"total":2,"data":[{"id":1,"name":"newspeed","contains_fields":[{"id":1,"name":"Official"}]},{"id":2,"name":"YAMAHA","contains_fields":[{"id":3,"name":"US"}]}]}
You are using your .map and Object.keys wrong
Look here at where you iterate over your Object keys properly :)
const data = {
total: 2,
data: [
{ id: 1, name: 'newspeed', contains_fields: [{ id: 1, name: 'Official' }] },
{ id: 2, name: 'YAMAHA', contains_fields: [{ id: 3, name: 'US' }] },
],
};
//now iterate over it properly
data.data.map((item) => {
Object.keys(item).map((key) => {
console.log(item[key]);
});
});
console.log will return this output
1
newspeed
[ { id: 1, name: 'Official' } ]
2
YAMAHA
[ { id: 3, name: 'US' } ]
I'm guessing you want to add the new data from your res.data to a state
So you can do something like this:
const fetchData = async () => {
try {
const res = await AdminListManager()
//do data manipulation over objects and set new state
} catch (error) {
showUnexpectedError()
}
}
useEffect(()=> {
fetchData()
}, [showUnexpectedError])

Can't perform a React state update on an unmounted component, using class component and component did mount

I have certain code as below:-
class BarChart extends Component {
constructor(){
super();
this.state = {
chartData:{}
}
}
componentDidMount() {
this.getChartData();
}
getChartData() {
axios.get("http://localhost:5001/inventory/clusterscount").then(res => {
const myresponse = res.data;
console.log(myresponse)
let countcluster = [];
let listregion = [];
for (const dataobj of myresponse){
countcluster.push(parseInt(dataobj.clusterscount));
listregion.push(dataobj.region);
}
console.log(countcluster)
console.log(listregion)
this.setState({
chartData: {
labels:listregion,
datasets: [
{
label: "level of thiccness",
data: countcluster,
backgroundColor: ["rgba(75, 192, 192, 0.6)"],
borderWidth: 4
}
]
}
});
});
}
render(){
return (
<div className="App">
<h1>Dankmemes</h1>
<div>
<Line
data={this.state.chartData}
options={{
responsive: true,
title: { text: "THICCNESS SCALE", display: true },
scales: {
yAxes: [
{
ticks: {
autoSkip: true,
maxTicksLimit: 10,
beginAtZero: true
},
gridLines: {
display: false
}
}
],
xAxes: [
{
gridLines: {
display: false
}
}
]
}
}}
/>
</div>
</div>
);
}
}
export default BarChart;
Now while running it am getting the desired clusters and regions as below:-
0: {clusterscount: '2', region: 'test1'}
1: {clusterscount: '10', region: 'test2'}
2: {clusterscount: '8', region: 'test3'}
3: {clusterscount: '1', region: 'test4'}
4: {clusterscount: '8', region: 'test5'}
5: {clusterscount: '2', region: 'test6'}
I am able to get results for clustercount and listregion as well, but keep getting this error. I have tried multiple things but out of ideas.
But in logs am getting as below:-
Can someone help me with this?
The react useEffect expects a cleanup function to cancel subscription and asynchronus tasks so we need to check if component is mounted or not there are couple of ways we can do it and react community have good solution for that.
const LineChart = () =>{
const [chartData,setChartData]= useState({});
const [myresponse, setmyresponse] =useState([]);
const isMountedRef = useRef(null);
useEffect(() =>{
isMountedRef.current = true;
let countcluster = [];
let listregion = [];
axios.get("http://localhost:5001/inventory/clusterscount").
then(res=>{
if(isMountedRef.current){
const myresponse= res.data;
setmyresponse(myresponse)
console.log(myresponse);
for (const dataobj of myresponse){
countcluster.push(parseInt(dataobj.clusterscount));
listregion.push(dataobj.region);
}
setChartData({
labels: listregion,
datasets: [
{
label: "level of thiccness",
data: countcluster,
backgroundColor: ["rgba(75, 192, 192, 0.6)"],
borderWidth: 4
}
]
});
}
})
.catch(err=>{
console.log(err);
});
return () => isMountedRef.current = false;
},[])

Adding object in Array in nested object with setState

I'd like to know how to add an object in datasets,
I'm trying to add an object in array with using setState,
but It doesn't work at all .
my code is like this :
const [DataContext, setDataContext] = useState([
{
labels: defaultLabels,
datasets: [
{
label: "dataSetting",
data: defaultDatas,
backgroundColor: defaultBackgroundColor,
},
],
},
{
labels: defaultLabels,
datasets: [
{
label: "dataSetting",
data: defaultDatas,
backgroundColor: defaultBackgroundColor,
},
],
},
{
labels: defaultLabels,
datasets: [
{
label: "dataSetting",
data: defaultDatas,
backgroundColor: defaultBackgroundColor,
},
],
},
const addAxis = index => {
let addAxis = [...DataContext];
addAxis[index].datasets.push({ label: "", data: "", background: "" });
setDataContext(addAxis);
};
You need a deep copy to update the state:
const addAxis = index => {
setDataContext(dataContext => dataContext.map((data, idx) => {
return idx === index ? {
...data,
datasets: [...data.datasets, { label: "", data: "", background: "" }]
} : data
})
};
You need to deep copy DataContext.
const _copy = (value) => {
let object;
if (Array.isArray(value)) {
object = [];
value.forEach((item, index) => {
if (typeof value[index] === 'object') {
object[index] = _copy(value[index]);
} else {
object[index] = value[index];
}
});
} else {
object = {};
for (let key in value) {
if (typeof value[key] === 'object') {
object[key] = _copy(value[key]);
} else {
object[key] = value[key];
}
}
}
return object;
};
const addAxis = index => {
let addAxis = _copy(DataContext);
addAxis[index].datasets.push({ label: "", data: "", background: "" });
setDataContext(addAxis);
};

Update a selected property from react state of objects with arrays

Assume that this state has initial data like this
const [options, setOptions] = useState({
ProcessType: [
{ value: 1, label: 'Type1' }, { value: 2, label: 'Type2' }
],
ResponsibleUser: [
{ value: 1, label: 'User1' }, { value: 2, label: 'User2' }
]
});
The following function will be called again and again when a post/put called
Help me to complete the commented area as described there.
const fetchProcesses = async () => {
await axios.get(`${process.env.REACT_APP_SERVER_BASE_URL}/processes/`)
.then((result) => {
/*
I want here to clear the existing data in options.ProcessType and
map result.data as { value: result.data.id , label: result.data.name },....
and push/concat it into to options.ProcessType but i want to keep the data
inside options.ResponsibleUser unchanged.
result.data is an array of objects like this,
[
{ id: 1 , name: 'Type1', desc : 'desc1', creator: 3, status: 'active' },
{ id: 2 , name: 'Type2', desc : 'desc2', creator: 6, status: 'closed' },
.....
.....
]
*/
})
}
Here is a solution
const fetchProcesses = async () => {
await axios.get(`${process.env.REACT_APP_SERVER_BASE_URL}/processes/`)
.then((result) => {
// solution
setOptions({ResponsibleUser: [...options.ResponsibleUser], ProcessType: result.data.map(row => ({value: row.id, label: row.name}))})
})
}

How to use spread operator to update array inside an object?

What the fetch returns is a list of items. I want to add those into state.
const [state, setState] = useState({
list: {
items: [],
}
});
fetch('http://example.com/list/')
// GET response: [{ name: 'foo' }, { name: 'bar' }, { name: 'baz' }]
.then((resList) => resList.json())
.then((list) => {
list.forEach(({ name }) => {
const itemUrl = `https://example.com/list/${name}`;
fetch(itemUrl)
// GET responses:
// { name: 'foo', desc: '123' }
// { name: 'bar', desc: '456' }
// { name: 'baz', desc: '789' }
.then((itemRes) => itemRes.json())
.then((item) => {
setState((prevState) => ({
...prevState,
list: {
items: [...state.list.items, item]
},
});
})
})
}
})
console.log(state);
// result: [{ name: 'baz', desc: '789' }]
// but wanted: [{ name: 'foo', desc: '123' }, { name: 'bar', desc: '456' }, { name: 'baz', desc: '789' }]
In your case no need to use prevState in setState.
I prepared an example for you. Just be careful at using hooks.
https://codesandbox.io/s/recursing-wood-4npu1?file=/src/App.js:0-567
import React, { useState } from "react"
import "./styles.css"
export default function App() {
const [state, setState] = useState({
list: {
items: [
{ name: "foo", desc: "123" },
{ name: "bar", desc: "456" },
],
},
})
const handleClick = () => {
setState(() => ({
list: {
items: [...state.list.items, { name: "baz", desc: "789" }],
},
}))
}
return (
<div className="App">
<button onClick={handleClick}>Click Me </button>
<hr />
{JSON.stringify(state)}
</div>
)
}
You can't directly access the callback for useState hooks. This is how you can update state after fetching the data:
setState({
...state,
list: {
items:[...state.list.items, item]
},
});

Resources