React inserting data to a nested array - arrays

I am doing a school project where i have to insert data to a nested array. I am able to insert data to a normal array. But i dont know how to insert to a nested array. As you see at the image answers is the nested array. I need to be able to insert data into name and votes that belongs to the nested array called answers. If you need more code, let me know!
Now for the code:
App.js
import React, { Component } from "react";
import { Router } from "#reach/router";
import Question from "./components/pages/Question";
import Questions from "./components/pages/Questions";
import Nav from "./components/layout/Nav";
import "./App.css";
import "bootstrap/dist/css/bootstrap.css";
import Ask from "./components/pages/Ask";
import Home from "./components/pages/Home";
export class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [
{
id: 1,
name: "nd time giving error,during TENSORFLOW execution",
answers: [
{
name:
"Observables are lazy so you have to subscribe to get the value.",
votes: 5
},
{ name: "You can use asyncPipe", votes: -2 },
{
name:
"The reason that it's undefined is that you are making an asynchronous operation",
votes: 3
}
]
},
{
id: 2,
name: "Is there a way to add/remove module in Android.bp?",
answers: [
{ name: "Answer 1", votes: 2 },
{ name: "Answer 2", votes: 3 },
{ name: "Answer 3", votes: 0 }
]
},
{
id: 3,
name: "Primeng p-dropdown filtering suggestions problem",
answers: [
{ name: "Answer 1", votes: 0 },
{ name: "Answer 2", votes: 1 }
]
},
{
id: 4,
name: "Configure CakePhp to send mail with SMTP",
answers: [
{ name: "Answer 1", votes: 0 },
{ name: "Answer 2", votes: 1 }
]
},
{
id: 5,
name: "CSS not working",
answers: [
{ name: "Answer 1", votes: 0 },
{ name: "Answer 2", votes: 1 }
]
}
]
};
}
getQuestion(id) {
return this.state.data.find(q => q.id === Number(id));
}
//This work, but only able to insert into data. Not answers
addQuestion(question) {
const questionObject = {
id: Math.random() * 10000000,
name: question,
answers: []
};
this.setState({
data: [...this.state.data, questionObject]
});
}
//With answerData, we can recieve the data to the parent, from the child.
//We get data from question. What the user have answered
getAnswer = answerData => {
//Last element in the array is the id from the question
const answerObject = {
name: "some-test-name",
answers: []
};
//Need to find the questionID, so we can parse the question to to the right answers
const getQuestionId = this.state.data.find(q => q.id === 1);
this.setState({
//This wont work...I am tryning to find the question that the answer belongs to.
answers: [...this.state.data.find(x => x.id === 1, answerObject)]
});
};
render() {
return (
<div className="App">
<Nav></Nav>
<Router>
<Ask
path="/Ask"
addQuestion={question => this.addQuestion(question)}
></Ask>
<Questions
path="/"
data={this.state.data}
askQuestion={text => this.askQuestion(text)}
></Questions>
<Question
path="/question/:id"
getQuestion={id => this.getQuestion(id)}
getAnswer={getAnswer => this.getAnswer(getAnswer)}
></Question>
</Router>
</div>
);
}
}
export default App;

All you need to keep track of your loop. Consider data is object.
let data = {
data: [
{
id: 1,
name: "nd time giving error,during TENSORFLOW execution",
answers: [
{
name: "Observables are lazy so you have to subscribe to get the value.",
votes: 5
},
{
name: "You can use asyncPipe",
votes: -2
},
{
name: "The reason that it's undefined is that you are making an",
votes: 3
}
]
}
]
}
Now you need to add some value to first object of answers array. You can do it like this
for(let obj of data.data) {
obj.answers[0]['isData'] = true;
console.log(obj.answers[0]);
}
isData property will be added into obj.answers[0].
And if you need to update the existing name or vote property, just override them.
for(let obj of data.data) {
obj.answers[0]['name'] = 'new name';
}

It didnt work, can some one help rewrite this method. postAnswer
postAnswer(questionID, name) {
const answerObject = {
name: name,
id: questionID
};
this.setState({
answers: [...this.state.data.find(x => x.id === questionID), answerObject]
});
//console.log(questionID, name);
}

Related

React Native FlatList error updating state "undefined is not an object (evaluating ......) [duplicate]

This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 4 days ago.
Building an app and needed to update the data state being rendered in a FlatList. Things were not working as I expected so I broke everything down to a simple example to better understand what was going on.
I feel like I am missing something. I determined from testing in this sample app that Flatlist is looking for an ordered key and has to be exactly ordered starting at 0 and so on...
My question is what am I missing? Is this actually a concrete requirement or do I something configured or coded incorrectly? I have not found any documentation stating this requirement of a very well ordered key.
Here is an example of the issue. If you click on the last item 44, you receive undefined is not an object as the lookup does not work to the state.
Full code example below;
Post
import React, { Component } from "react";
import { useState } from "react";
import { Button, UIManager, Platform, LayoutAnimation, Modal, Alert, Linking, Pressable, View, Text, StyleSheet, TouchableHighlight, Image, TextInput, FlatList, ActivityIndicator, Dimensions, SafeAreaView, TouchableOpacity, } from "react-native";
import { FlashList } from "#shopify/flash-list";
import { List, ListItem, Avatar } from "react-native-elements";
import { StatusBar } from "expo-status-bar";
import ParsedText from "react-native-parsed-text";
import AntDesign from "react-native-vector-icons/AntDesign";
class Post extends Component {
shouldComponentUpdate(nextProps, nextState) {
const { liked, likeCount } = nextProps;
const { liked: oldLiked, likeCount: oldLikeCount } = this.props;
// If "liked" or "likeCount" is different, then update
return liked !== oldLiked || likeCount !== oldLikeCount;
}
render() {
console.log(this.props.liked);
return (
<View key={this.props.postid} style={styles.container}>
<TouchableOpacity
onPress={() => this.props.onPressLike(this.props.postid)}
>
<AntDesign
name="like2"
size={20}
color={this.props.liked ? "royalblue" : "black"}
/>
</TouchableOpacity>
<Text> {this.props.postid}</Text>
</View>
);
}
}
Home
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
posts: [
{
postid: "0",
date: "115255668551",
message: "My Post 1 was 4",
uid: "52YgRFw4jWhYL5ulK11slBv7e583xa",
commentCount: "1",
likeCount: "1",
liked: "true",
},
{
postid: "1",
date: "215255668551",
message: "My Post 2",
uid: "52YgRFw4jWhYL5ulK11slBv7e583xb",
commentCount: "1",
likeCount: "1",
liked: "false",
},
{
postid: "2",
date: "315255668551",
message: "My Post 3",
uid: "52YgRFw4jWhYL5ulK11slBv7e583xc",
commentCount: "1",
likeCount: "1",
liked: "true",
},
{
postid: "3",
date: "415255668551",
message: "My Post 4",
uid: "52YgRFw4jWhYL5ulK11slBv7e583xd",
commentCount: "1",
likeCount: "1",
liked: "false",
},
{
postid: "44",
date: "515255668551",
message: "My Post 44 does not work!!!!!!!!!!!!!!!!!",
uid: "52YgRFw4jWhYL5ulK11slBv7e583xe",
commentCount: "1",
likeCount: "1",
liked: "false",
},
],
postsThisIsWorking: [
{
postid: "0",
date: "115255668551",
message: "My Post 0",
uid: "52YgRFw4jWhYL5ulK11slBv7e583xa",
commentCount: "1",
likeCount: "1",
liked: "true",
},
{
postid: "1",
date: "215255668551",
message: "My Post 1",
uid: "52YgRFw4jWhYL5ulK11slBv7e583xb",
commentCount: "1",
likeCount: "1",
liked: "false",
},
{
postid: "2",
date: "315255668551",
message: "My Post 3",
uid: "52YgRFw4jWhYL5ulK11slBv7e583xc",
commentCount: "2",
likeCount: "1",
liked: "true",
},
{
postid: "3",
date: "415255668551",
message: "My Post 3",
uid: "52YgRFw4jWhYL5ulK11slBv7e583xd",
commentCount: "1",
likeCount: "1",
liked: "false",
},
{
postid: "4",
date: "515255668551",
message: "My Post 4",
uid: "52YgRFw4jWhYL5ulK11slBv7e583xe",
commentCount: "1",
likeCount: "1",
liked: "false",
},
],
};
}
renderItem = ({ item, index }) => {
const { date, message, uid, postid, liked } = item;
console.log("---index-----", index);
return (
<Post
postid={postid}
liked={liked}
date={date}
message={message}
uid={uid}
onPressLike={this.handleLikePost}
/>
);
};
handleLikePost = (postid) => {
console.log("----posts----", this.state.posts);
console.log("---postid---", { postid });
let post = this.state.posts[postid]; //error with undefined (evaluating post liked) ----
console.log("----post----", post);
const { liked, likeCount } = post;
const newPost = {
...post,
liked: !liked,
likeCount: liked ? likeCount - 1 : likeCount + 1,
};
this.setState({
posts: {
...this.state.posts,
[postid]: newPost,
},
});
};
render() {
console.log(this.state.posts);
return (
<View style={{ flex: 1 }}>
<FlatList
data={Object.values(this.state.posts)}
renderItem={this.renderItem}
keyExtractor={(item, postid) => postid}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 50,
backgroundColor: "#ecf0f1",
padding: 8,
},
});
This is nothing to do with FlatList, or with React for that matter. Your posts is defined as an array in the constructor, but you're trying to access it as an object (this.state.posts[postid]). (Note that postid contains string values, not numbers, which would have allowed array access to work like that if the postids were always sequential integers starting with zero. Though that seems unlikely in real usage.)
To access it like that, your data structure should be
posts: {
"0": {postid: "0", likeCount: 1, etc: "etc"},
"1": {postid: "1", likeCount: 1, etc: "etc"}
...
}
(Later in your code you have a setState that also assumes posts is an object using the above format; it's only the state data in your constructor that's in an array.)
The initial state of the posts is a array. When you're updating the state in handleLikePost you're setting the posts to a object.
You could do something like this to work with the array. Map over all the posts and modify the post if the id is the same. This will return a new array which you can then set to the state.
handleLikePost = (postidToUpdate) => {
const updatedPosts = this.state.posts.map((post) => {
if (post.postid !== postidToUpdate) return post;
const updatedLikeCount = post.liked
? parseInt(post.likeCount) - 1
: parseInt(post.likeCount) + 1;
return {
...post,
liked: !post.liked,
likeCount: updatedLikeCount.toString(),
};
});
this.setState({
posts: updatedPosts,
});
};
I updated the likeCount to parse the int for adding and subtracting. And when returning the new post convert it back to a string.
I was able to figure out how to properly do the lookup on the object, I needed to use the index which is assigned starting at 0 as the rows are rendered. Works perfectly now...
handleLikePost = index => {
let post = this.state.posts[index] //lookup by index
const { liked, likeCount } = post
const newPost = {
...post,
liked: !liked,
likeCount: liked ? likeCount - 1 : likeCount + 1
}
this.setState({
posts: {
...this.state.posts,
[index]: newPost
}
})
}

NextJs / React: Organizing Array

I'm having issues understanding how to best manipulate an array to get the data I want. From the research I've done, there's multiple ways, but I'm unclear on which is most optimized.
I want to display a simple list, with the items broken down by country, then state, then organized alphabetically by city. The array is formatted as follows:
[
{
id: 1,
name: "Place 1",
state: "Florida",
city: "Boca Raton",
country: "US",
},
{
id: 2,
name: "Place 2",
state: "Florida",
city: "Daytona Beach",
country: "US",
},
{
id: 3,
name: "Place 3",
state: "Kansas",
city: "Lenexa",
country: "US",
},
{
id: 4,
name: "Place 4",
state: "Harju",
city: "Tallinn",
country: "EE",
},
]
An example of the desired outcome is:
US
Florida
Place 1
Place 2
Kansas
Place 3
EE
Harju
Place 4
I see a lot of people saying to utilize ES6 for this, but I'm not sure the best way to approach it. Manipulate the original array response? Is there some way I can loop through them?
Here's an approach that only requires a single loop.
const data = [];
let result = {};
data.forEach(({ name, state, country }) => {
if (!result[country]) {
result[country] = {};
}
if (!result[country][state]) {
result[country][state] = [name];
}
else {
result[country] = {
...result[country],
[state]: [
...result[country][state],
name
]
};
}
});
console.log(result);
Output
{
US: { Florida: [ 'Place 1', 'Place 2' ], Kansas: [ 'Place 3' ] },
EE: { Harju: [ 'Place 4' ] }
}
I'm sure the if-else part can be removed by using spread operator and operator chaining, but I wasn't able to figure that out.
If your environment supports operator chaining, here's a smaller solution
const data = [];
let result = {};
data.forEach(({ name, state, country }) => {
result[country] = {
...result[country],
[state]: [
...(result?.[country]?.[state] || []),
name
]
};
});
console.log(result);

React - setState with certain index

I've been stuck for whole day and please help me to fix it.
I have a json data which like this :
[
{
"menu": "menu_1",
"icon": "icon_1",
"detail": {
"name": "name_1",
"phone": "phone_1"
}
},
{
"menu": "menu_2",
"icon": "icon_2",
"detail": {
"name": "name_2",
"phone": "phone_2"
}
},
{
"menu": "menu_3",
"icon": "icon_3",
"detail": {
"name": "name_3",
"phone": "phone_3"
}
}
]
I put them into the "data" state and My goal is I wanna change the "detail" state with certain index ( ex: state "data" with index 1 change the "detail" data )
Currently my code is :
this.setState({
data: {
...this.state.data,
detail:{
this.state.data[1].detail:{
"name": "billy",
"phone": "893823839"
}
}
}
})
That setState is clearly wanna change the state with certain index but fail..
How do I supposed to do?
I guess this is what you're looking for, we could replace an element inside an array using splice :
const index = 1;
this.setState({
data: [...this.state.data].splice(index, 1, {
...this.state.data[index],
details: { name: "billy", phone: "893823839" },
}),
});
Update: we could use slice also to make an immutable update with index :
this.setState({
data: [
...this.state.data.slice(0, index),
{
...this.state.data[index],
details: { name: "billy", phone: "893823839" },
},
...this.state.data.slice(index + 1, this.state.data.length),
],
});
could you try it ?
this is an example that i tested using splice:
const items = [{ id: 1 }, { id: 2 }, { id: 3 }];
const indexToBeModified = 1; // { id: 2 } ==> { foo: "foo", id: 2 }
items.splice(indexToBeModified, 1, { ...items[indexToBeModified], foo: "foo" });
console.log("items", items);
Here is a little modified example. It uses prevState to prevent any unwanted changes that may happen when directly interacting with this.state.
import React, { Component } from "react";
export default class App extends Component {
constructor() {
super();
this.state = {
data: [
{
menu: "menu_1",
icon: "icon_1",
detail: {
name: "name_1",
phone: "phone_1"
}
},
{
menu: "menu_2",
icon: "icon_2",
detail: {
name: "name_2",
phone: "phone_2"
}
},
{
menu: "menu_3",
icon: "icon_3",
detail: {
name: "name_3",
phone: "phone_3"
}
}
]
};
this.modifyData = this.modifyData.bind(this);
}
modifyData(index) {
this.setState((prevState) => {
prevState.data[index].detail={
name: "billy",
phone: "893823839"
};
return {
data: [prevState.data]
};
},()=>{console.log(this.state.data)});
}
render() {
return (
<button onClick={() => this.modifyData(0)}>Click to modify data</button>
);
}
}
Here is a code sandbox reference.

React Axios Get Call to Output JSON Format

I am performing an Axios get call in a React Component to retrieve JSON info. That function is working great. Within the JSON is a label for various network ports, which are returning as an array in my axios call. These are ultimately going to be displayed as nodes on a d3 graph. My issue is that I need to output the data pulled from the get call into the following format:
nodes: [
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' }
]
So the full component for the graph to read is:
export const data = {
nodes: [
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' },
{ id: 'JSON data.label here' }
]
}
Here is the format of the Axios get I am using:
axios.get(`NetworkConstruct.json`)
.then(res => {
const names = res.data.items;
this.setState({ names });
});
Here is a sample output I am receiving (there are 11 of these):
{id: "5bc0860c-ece1-461c-bac0-b155a3cacd82", label: "80.107.0.212",
resourceTypeId: "tosca.resourceTypes.NetworkConstruct", productId:
"5bc0835c-6cfa-486e-8429-a59eaf4118bc", tenantId: "393fa8da-61fd-458c-80f9-
ce92d0ef0330", …}
The data has to be in this EXACT format or the graph won't read it. I'm guessing I'll need to do an initial map function but am stuck on how to arrange it. I cannot have any divs or quotes in my output. Is this doable? I have scoured the boards and Google for a couple of days and can't make this work yet.
Here is the object I am receiving from the GET request.
{
"id": "5bd2c6ef-6009-4b90-9156-62168f3c6293",
"resourceId": "5bd0ba82-2994-455d-8716-2adb5694d6f0",
"interface": "getGraph",
"inputs": {},
"outputs": {
"graph": {
"nodes": [
{
"id": "5bcdf06c-dd53-4335-840f-55a4b8d85a2d",
"name": "asw-lab9306b",
"ports": {
"GigabitEthernet3/0/8": "5bd1777f-0ab9-4552-962b-9e306ce378ab",
"GigabitEthernet2/0/15": "5bd1777e-119c-44e8-ba69-0d86a481c0f5",
"GigabitEthernet3/0/47": "5bd17783-be94-4aaf-8858-70e4eb3d02dc",
"GigabitEthernet2/0/13": "5bd17783-ed99-453f-a958-f764edaa8da8"
}
}
],
"links": [
{
"a": "5bd1a467-13f2-4294-a768-561187b278a8",
"z": "5bd17770-2e6c-4c37-93c8-44e3eb3db6dd",
"layer": "ETHERNET"
},
{
"a": "5bd1776e-c110-4086-87d6-a374ccee419a",
"z": "5bd17770-83ee-4e10-b5bb-19814f9f5dad",
"layer": "ETHERNET"
}
]
}
},
"state": "successful",
"reason": "",
"progress": [],
"providerData": {},
"createdAt": "2018-10-26T07:49:03.484Z",
"updatedAt": "2018-10-26T07:49:25.425Z",
"resourceStateConstraints": {},
"executionGroup": "lifecycle"
}
The info I need is the nodes ID. There are eleven of them in the full object.
You can map an array of objects to another array of objects in your format with Array.prototype.map(). Assuming that data is the list of objects from your response:
class Graph extends React.Component {
state = {
nodes: null,
};
componentDidMount() {
axios.get('the url').then(response => {
const nodes = response.data.outputs.graph.nodes;
this.setState({nodes});
});
}
render() {
const {nodes} = this.state;
if (!nodes) return 'Loading...'
return <TheD3ComponentYouUse nodes={nodes} />;
}
}

How can I mutate my state without having to reload the entire json object via the API

I am using reactjs with redux, and on the UI I am displaying categories along with the products associated to them.
When the user moves a product to another category, I don't want to have to reload the entire JSON file as I am doing currently because the JSON is actually pretty large.
How can I mutate this JSON file by just removing the productId from the catProducts collection of one category and adding the productId to another categories catProducts collection.
{
"shop" : {
"categories": [
{
id: 1,
name: "category1",
"catProducts" : [
{
productId: 123
}
]
},
{
id: 2,
name: "category2",
"catProducts" : [
{
productId: 456
}
]
},
{
id: 3,
name: "category3",
"catProducts" : [
{
productId: 789
}
]
}
]
}
}
Below is my action and reducer, as you can see I am reloading the entire category with products each time a product is moved. All I need to do is after the API call is made, mutate my state by removing the productId from one catProducts and add it to another. How can I do this?
Actions:
moveProduct: (data) => {
return dispatch => {
var url = '....'; // api to move the product in db
axios.post(url, reqArgs)
.then(function(resp) {
dispatch(Actions.fetchCategoriesAndProducts(data.shopId));
});
};
},
Reducer:
case Constants.FETCH_CATEGORIES_AND_PRODUCTS:
return {...state, fetching: false, shop: action.data };
Given that you have an index fromIndex representing the categories array index to move a product from, toIndex representing the categories array index to move a product to, and productId of the product to move, you could do it like this:
const data = {
shop: {
categories: [
{
id: 1,
name: "category1",
catProducts: [
{
productId: 123
}
]
},
{
id: 2,
name: "category2",
catProducts: [
{
productId: 456
}
]
},
{
id: 3,
name: "category3",
catProducts: [
{
productId: 789
}
]
}
]
}
};
const fromCategoryIndex = 0;
const toCategoryIndex = 1;
const productId = 123;
const categories = [...data.shop.categories];
categories[fromCategoryIndex] = {
...categories[fromCategoryIndex],
catProducts: categories[fromCategoryIndex].catProducts.filter(
product => product.productId !== productId
)
};
categories[toCategoryIndex] = {
...categories[toCategoryIndex],
catProducts: [...categories[toCategoryIndex].catProducts, { productId }]
};
console.log(categories);

Resources