Can't setState in react handleClick - javascript

I am unable to setState in handleClick function for this App.
App is working very simple, it has a data that has an entry point in forest from there it just goes thru props till it gets to Tree component where divides to Children and Datum there it has a Collapsible that takes care of the expanding logic.
Now,
I just want to mutate state I have done many copies of that state with JSON.parse(JSON.Stringify())) used Lodash for deep copy, everything works expect that copied and mutated state just doesn't want to set into setState so I can't get it to circular working.
I just want to have a unidirectional data flow, but the 'setState' just doesn't work.
The function that mutates the state mutateState takes as arguments a forest a node.id a patch or get as a string value. I have tested it million times and it works, this function mutates the state via recursion. One thing maybe it has a problem that it doesn't return anything, even if it doesn't return it still doesn't want to set that 'setState'.
I have also used debugger million times and didn't find any clue. Please I need some help.
thank you.
import "./App.css";
import { useState, useEffect, useContext, createContext } from "react";
import React from "react";
import data from "./data.json";
import _ from "lodash";
function App() {
const [state, setState] = useState(data.data);
function mutateState(forest, nodeId, getOrPatch) {
let result;
forest.forEach(function (tree) {
if (tree.id === nodeId && getOrPatch === "patch") {
tree.toggle === true ? (tree.toggle = false) : (tree.toggle = true);
} else if (tree.id === nodeId && getOrPatch === "get") {
return (result = tree.toggle);
}
helper(tree);
});
function helper(tree) {
if (tree.children !== null) {
tree.children.forEach(function (tree) {
if (tree.id === nodeId && getOrPatch === "patch") {
tree.toggle === true ? (tree.toggle = false) : (tree.toggle = true);
} else if (tree.id === nodeId && getOrPatch === "get") {
return (result = tree.toggle);
}
helper(tree);
});
}
}
return result;
}
function Forest({ root }) {
return root.map((tree) => <Tree root={tree} key={tree.id} />);
}
function Tree({ root }) {
if (root.children !== null) {
return <Children key={root.id} node={root} />;
} else {
return <Datum key={root.id} node={root} />;
}
}
function Collapsible(props) {
return (
<ul>
<button onClick={props.handleClick} className="btn">
<li>{props.datum}</li>
</button>
{props.isExpanded ? props.children : ""}
</ul>
);
}
function Children({ node }) {
const [isExpanded, setIsExpanded] = useState(false);
let stateCopy = _.cloneDeep(state)
function handleClick() {
mutateState(stateCopy, node.id,'patch')
setState(stateCopy)
isExpanded ? setIsExpanded(false) : setIsExpanded(true);
}
return (
<Collapsible
datum={node.datum}
handleClick={handleClick}
isExpanded={isExpanded}
>
{node.children.map((tree) => (
<Tree key={tree.id} root={tree} />
))}
</Collapsible>
);
}
function Datum({ node }) {
return <li>{node.datum}</li>;
}
return (
<div className="App">
<header className="App-header">
<Forest root={state} />
</header>
</div>
);
}
export default App;
data JSON
{
"data": [
{
"datum": "String",
"id": 1,
"toggle": false,
"children": [
{
"datum": "String",
"id": 2,
"toggle": false,
"children": [
{
"datum": "String",
"id": 3,
"toggle": false,
"children": []
}
]
},
{
"datum": "String",
"id": 4,
"toggle": false,
"children": []
}
]
},
{
"datum": "String",
"id": 5,
"toggle": false,
"children": [{
"datum": "String",
"id": 6,
"children": []
}]
},
{
"datum": "String",
"id": 7,
"toggle": false,
"children": [
{
"datum": "String",
"id": 8,
"toggle": false,
"children": [
{
"datum": "String",
"id": 9,
"toggle": false,
"children": [
{
"datum": "String",
"id": 10,
"toggle": false,
"children": [
{
"datum": "String",
"id": 11,
"toggle": false,
"children": []
}
]
}
]
},
{
"datum": "String",
"id": 12,
"toggle": false,
"children": [
{
"datum": "String",
"id": 13,
"toggle": false,
"children": []
}
]
}
]
},
{
"datum": "String",
"id": 14,
"toggle": false,
"children": []
}
]
}
]
}

Related

State in React Native Class Component won't update when I update object in array

I'm using React Navigation to send the array additional in the DisplayItem screen, I use componentDidMount to set the state with the array and I use SectionList to display the array, the goal is to use the function pressOptional to update the value selected in the array, It works at the moment, but the problem is that it doesn't update the state in real-time, the changes in SectionList happen only when I re-render the screen.
Array: additional
Array [
Object {
"data": Array [
Object {
"id": 0,
"price": 1,
"selected": true,
"title": "Ranch",
"type": "Sides",
},
Object {
"id": 1,
"price": 1,
"selected": false,
"title": "Blue Cheese",
"type": "Sides",
},
],
"id": 0,
"required": false,
"title": "Sides",
},
Object {
"data": Array [
Object {
"id": 0,
"price": 0,
"selected": false,
"title": "Hot Sauce",
"type": "Sauces",
},
Object {
"id": 1,
"price": 0,
"selected": false,
"title": "Medium Sauce",
"type": "Sauces",
},
],
"id": 1,
"required": true,
"title": "Sauces",
},
]
Screen
class DisplayItem extends Component {
constructor(props) {
super(props);
this.state = {
additional: [],
}
}
componentDidMount() {
const { item } = this.props.route.params;
const additional = item.additional;
if (additional !== undefined) {
this.setState({additional:[...additional]})
}
}
pressOptional = (e) => {
const additional = this.state.additional;
additional.map((item) => {
if (item.title == e.type) {
item.data.map((item) => {
if (item.id == e.id) {
item.selected = !e.selected
}
})
}
})
}
render() {
const { additional } = this.state;
return (
<View>
<SectionList
sections={additional}
keyExtractor={(item, index) => item + index}
renderSectionHeader={({ section: { type, required } }) => (
<View>
<Text>{type}</Text>
</View>
)}
renderItem={({ item, section: { required } }) => {
return (
<TouchableOpacity key={item.id} onPress={() => this.pressOptional(item)}>
<Ionicons
name={item.selected == false ? 'square-outline' : 'square'}
/>
<Text>{item.title}</Text>
<Text>${item.price}</Text>
</TouchableOpacity>
)
}
}
/>
</View>
)
}};
export default DisplayItem;
Try to use useState instead State classes:
https://reactnative.dev/docs/state

How to check if array is empty or not and return value inside foreach in angular8

I have below data,
What i want to do is, i need to check values[] array in each object,
if it is empty then return true, else if values[] array has some record, it will return false.
i have created function for this, but it is reuturning false everytime.
if it is true thn i want to hide table. Table are multiple not single one, so if values arrayis empty thn only want to hide particular table.
{
"records": [
{
"context": {
"team": [
"MYTEAM-Consume-TEAM-SETUP-ADMIN"
]
},
"values": []
},
{
"context": {
"team": [
"TEAM1"
]
},
"values": [
{
"value": "red",
"label": "dd"
}
]
},
{
"context": {
"team": [
"Test"
]
},
"values": []
},
]
}
Code
hideContextTable(rows) {
const data = rows;
if (data.records) {
data.records.forEach(function (record) {
if (record.values.length === 0) {
return true;
}
});
}
return false;
}
deleteAllContextData(data) {
const tbl = this.hideContextTable(data);
console.log(tbl,"tbl");
if (tbl) {
this.showContextTables = false;
}
}
Simply check the length of returned data from filter function.
data = {
"records": [
{
"context": {
"team": [
"MYTEAM-Consume-TEAM-SETUP-ADMIN"
]
},
"values": []
},
{
"context": {
"team": [
"TEAM1"
]
},
"values": [
{
"value": "red",
"label": "dd"
}
]
},
{
"context": {
"team": [
"Test"
]
},
"values": []
},
]
};
function hideContextTable(data) {
const result = data.records.filter(record => record.values.length);
const flag = result.length ? true : false;
console.log(flag)
}
hideContextTable(data);
return in the higher order function of forEach will not cause flow to leave the hideContextTable function. You should use a variable that is accessible from outside that function and set it if the condition is met, then return that variable at the end of the function.
const rows = {
"records": [
{
"context": {
"team": [
"MYTEAM-Consume-TEAM-SETUP-ADMIN"
]
},
"values": []
},
{
"context": {
"team": [
"TEAM1"
]
},
"values": [
{
"value": "red",
"label": "dd"
}
]
},
{
"context": {
"team": [
"Test"
]
},
"values": []
},
]
}
function hideContextTable(rows) {
let isEmpty = false;
const data = rows;
if (data.records && data.records.values) {
data.records.forEach(function (record) {
if (record.values.length === 0) {
isEmpty = true;
return; // this is a higher order function
// meaning: it won't leave the context of hideContextTable
}
});
}
return isEmpty;
}
const test = hideContextTable(rows);
console.log(test);

Calling Find Function Twice gives undefined as output

Following code works fine the first time, It finds the correct item and changes its checked value, but if I call the same function with the same id again it returns undefined. any idea why?
This code is using in a React Native Application where the checkbox is updated using this method
CheckBox :
<CheckBox value={item.checked} key={item.id} onValueChange={setSelection} onChange={()=> {
handleChange(item.id);
}}
style={styles.checkbox}
tintColors={{ true: "#ffc324", false: "#ffc324" }}
/>
const handleChange = (id) => {
const ids = id;
let changedCheckbox = categories.find((category) => {
return category.subcategory.find((item) => {
if (item.id === ids) {
return (item.checked = !item.checked);
}
});
});
console.log(changedCheckbox);
};
This is the JSON I use
[
{
"id": 1,
"name": "MOTs",
"filename": "1610270182.png",
"bg_filename": null,
"content": "You can try this set a state like this and check if your component mounted or not. This way you are sure that if your component is unmounted you are not trying to fetch something.",
"featured": 1,
"created_at": "2021-01-10T09:16:22.000000Z",
"updated_at": "2021-01-10T09:40:37.000000Z",
"subcategory": [
{
"id": 1,
"name": "MOT1",
"category_id": 1,
"image_name": null,
"created_at": null,
"updated_at": null,
"checked": false
},
{
"id": 2,
"name": "MOT2",
"category_id": 1,
"image_name": null,
"created_at": null,
"updated_at": null,
"checked": false
},
{
"id": 3,
"name": "MOT3",
"category_id": 1,
"image_name": "1611678308.png",
"created_at": "2021-01-26T16:25:11.000000Z",
"updated_at": "2021-01-26T16:31:24.000000Z",
"checked": false
}
]
}
]
const handleChange = (id) => {
const category = categories.find(category => {
const item = category.subcategory.find(item => item.id === id);
if (item) item.checked = ! item.checked;
return !!item;
});
console.log(category)
};
Toggle checked then return boolean not checked.
change this statement
return (item.checked = !item.checked);
what you are doing when it is false it will return true, the next time its value is true so it will return false
do this instead
if (item.id===ids){
item.checked = !item.checked;
return true;
}

How to display nested array of objects from REST call in different sections of the page?

I have a REST call which returns different sets of result. Each data set contains data for different parts of the page. It contains nested array of objects and I am not sure how do I easily display the name and value pairs from the rest call to my page depending on the certain section.
Here's what the REST call response looks like:
{
"dstrSpecificHelpRs": [
{
"row": [
{
"name": "HELP_TEXT_TITLE_NM",
"value": "TestAgain and again and again and again andagain50",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
},
{
"name": "HELP_TEXT_ID",
"value": "100114",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
},
{
"name": "Help",
"value": "Help",
"link": {
"exists": true,
"linkType": "url",
"linkId": "www.google.com"
}
}
]
},
{
"row": [
{
"name": "HELP_TEXT_TITLE_NM",
"value": "Testing Helpline Page and 2301 DisasterONLY",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
},
{
"name": "HELP_TEXT_ID",
"value": "100174",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
},
{
"name": "Help",
"value": "Help",
"link": {
"exists": true,
"linkType": "url",
"linkId": "www.google.com"
}
}
]
}
],
"rgstInfoRs": [
{
"row": [
{
"name": "PREFIX_NM",
"value": "MS",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
},
{
"name": "FST_NM",
"value": "MITZI",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
}
]
}
],
"insSettlementRs": [
{
"row": [
{
"name": "ON_FILE",
"value": "0",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
}
]
}
],
"dstrOptionRs": [
{
"row": [
{
"name": "DSTR_OPTION",
"value": "1",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
},
{
"name": "HELPLINE_AREA_CD",
"value": "818",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
},
{
"name": "HELPLINE_PHN_NR",
"value": "5055511",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
}
]
}
],
"correspondenceOutRs": [
{
"row": [
{
"name": "SUMMARY_CD_TX",
"value": "SUPER,SEAL,APPR_INTRO,APPROVAL,ECNA",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
}
]
},
{
"row": [
{
"name": "SUMMARY_CD_TX",
"value": "9069CL,SEAL",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
},
{
"name": "ASTN_PGM_CD",
"value": "MISC",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
},
{
"name": "GENERATED_DT",
"value": "2020-09-03 14:08:10.0",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
}
]
}
],
"statePhoneRs": [],
"insAssistanceRs": [
{
"row": [
{
"name": "DOC_ID",
"value": "",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
},
{
"name": "RGSN_ID",
"value": "370002900",
"link": {
"exists": false,
"linkType": null,
"linkId": null
}
}
]
}
]
}
and here's what I have working so far:
class App extends React.Component {
constructor(props) {
super(props);
this.setState = {
data: []
};
}
componentDidMount() {
fetch("https://demo3531217.mockable.io/getRegistrationData")
.then((res) => res.json())
.then((res) => {
const responseData = this.getPayloadObject(res);
this.setState({
data: responseData
});
})
.catch((error) => console.error(error));
}
getPayloadObject = (action) => {
console.log(action);
Object.keys(action).forEach((key) => {
if (typeof action[key] === "object") {
const payload = action[key];
let result = _.map(
payload,
(data, i) => {
const row = data["row"];
const rowData = _.map(row, (item) => {
return {
name: item.name,
value: item.value
};
});
console.log("rowData ", rowData);
},
[]
);
return result;
}
});
};
render() {
return (
<div>
<div>
<h1>Section 1</h1>
</div>
<div>
<h1>Section 2</h1>
</div>
</div>
);
}
}
if I wanted to display certain data for specific section how do I so in my page? Can someone please help?
link for sandbox can be found here: https://codesandbox.io/s/vibrant-sid-i140l?file=/src/App.js:75-1304
Let's try breaking the state into multiple sections. Something like this.
constructor(props) {
super(props);
this.state = {
dstrSpecificHelpRs: [],
rgstInfoRs: [],
insSettlementRs: [],
dstrOptionRs: [],
correspondenceOutRs: [],
statePhoneRs: [],
insAssistanceRs: []
};
}
Then, let's deconstruct the API response and set individual items in the state.
componentDidMount() {
fetch("https://demo3531217.mockable.io/getRegistrationData")
.then((res) => res.json())
.then((res) => {
// const responseData = this.getPayloadObject(res);
const {
dstrSpecificHelpRs,
rgstInfoRs,
insSettlementRs,
dstrOptionRs,
correspondenceOutRs,
statePhoneRs,
insAssistanceRs
} = res;
this.setState({
dstrSpecificHelpRs: dstrSpecificHelpRs,
rgstInfoRs: rgstInfoRs,
insSettlementRs: insSettlementRs,
dstrOptionRs: dstrOptionRs,
correspondenceOutRs: correspondenceOutRs,
statePhoneRs: statePhoneRs,
insAssistanceR: insAssistanceRs
});
})
.catch((error) => console.error(error));
}
Now all you have to do is find a way to loop through individual items in the state. Something like this
render() {
const dstrSpecificHelpRs = this.state.dstrSpecificHelpRs.map(row => row["row"]);
console.log("Sample = ", sample);
return (
<div>
<div>
<h1>Section 1</h1>
<div>
{dstrSpecificHelpRs.map(item => item.map(key => <p>{key.name}</p>))}
</div>
</div>
<div>
<h1>Section 2</h1>
</div>
</div>
);
}
This is by no means a perfect solution though. If you could change the response model, that would have been sweet!.

How to access a json key in ReactJS?

i have a render problem, in componentDidMount i use axios for a get call, after i assign the json with setstate
the problem is:
when i try to call json's key in render, they are undefined.. bacause componentdidmount works after render..
for example:
export default class ShowDetails extends Component {
constructor(props) {
super(props);
this.accountService=new AccountService();
this.state = {
account:[]
}
}
componentDidMount() {
this.accountService.getAccount().then(r => this.setState({ account: r.data })); }
render() { return ( {this.state.account.all.moneyavailable.Formatted} //this is undefined
I have alredy tried to move in other place the axios call, but without success
the json:
{
"account": [
{
"saldoDisponibileFormatted": "1.664,57",
"saldoContabileFormatted": "1.341,10",
"value": "EUR",
"debit": false,
"productAlias": null,
"bank": "bank",
"iban": "IT31M0326801017052670700860",
"moviments": [
{
"causShort": "Commissioni",
"import": "1,00",
"sign": "D"
},
],
"dateUpdate1": "n/d",
"dateUpdate2": "4/12/2018"
},
],
"all": {
"moneyavailable": {
"Formatted": "1950,82"
},
"moneyavailable2": {
"Formatted": "31627,35"
},
"dateUpdate": "4/12/2018"
}
}
To access properties on your object you can do this:
const data = {
"account": [
{
"saldoDisponibileFormatted": "1.664,57",
"saldoContabileFormatted": "1.341,10",
"value": "EUR",
"debit": false,
"productAlias": null,
"bank": "bank",
"iban": "IT31M0326801017052670700860",
"moviments": [
{
"causShort": "Commissioni",
"import": "1,00",
"sign": "D"
},
],
"dateUpdate1": "n/d",
"dateUpdate2": "4/12/2018"
},
],
"all": {
"moneyavailable": {
"Formatted": "1950,82"
},
"moneyavailable2": {
"Formatted": "31627,35"
},
"dateUpdate": "4/12/2018"
}
}
console.log(data.all) // for the all key
console.log(data.account) // for the account key
console.log(data.all.moneyavailable.Formatted)
Assuming your json is assigned to a property in your state called jsonData, you can access that property like this:
this.state.jsonData.all.moneyavailable.Formatted
Check the snippet below:
var jsonData = {
"account": [
{
"saldoDisponibileFormatted": "1.664,57",
"saldoContabileFormatted": "1.341,10",
"value": "EUR",
"debit": false,
"productAlias": null,
"bank": "bank",
"iban": "IT31M0326801017052670700860",
"moviments": [
{
"causShort": "Commissioni",
"import": "1,00",
"sign": "D"
},
],
"dateUpdate1": "n/d",
"dateUpdate2": "4/12/2018"
},
],
"all": {
"moneyavailable": {
"Formatted": "1950,82"
},
"moneyavailable2": {
"Formatted": "31627,35"
},
"dateUpdate": "4/12/2018"
}
}
console.log(jsonData.all.moneyavailable.Formatted);
alert(jsonData.all.moneyavailable.Formatted);
jsFiddle: http://jsfiddle.net/AndrewL64/rh8d0aqz/6/
Just store json in one variable.
var data = { ... json};
console.log(data.all.moneyavailable);

Categories