Using React I am trying to pass down a parsed JSON file and map through the object. However seeing as it is an object I can only map the object using Object.keys as below:
const question = Object.keys(questions).map((data, index) =>
<QuestionList questions={data} key={index} />
However using this I am only able to access the top level of my data structure. So using this code and passing down my object. Using Object.keys() I can only access "questions" or "q1" or type, text, qId etc. I can not pass all object properties at once and specify what I need in a child component.
"questions": {
"q1": {
"qId": "1",
"type": "YN",
"text": "Text 1",
"tip": {
"logo": "assets/icons/gadgetz.svg",
"logofallback": "assets/img/gadgetz.png",
"heading": "Heading 1",
"text": "Text 2"
}
},...
What would be the easiest way to pass the whole object with child properties so I can use these in a child component? do I have to use something other than props?
const questionObjects = Object.values(JSON.stringify(theJSONinYourQuestion));
const questionComponents = questionObjects.map(question => <Question qId={question.qId} />);
Basically, use Object.values instead of Object.keys, and you've got a nice array of questions you can use.
Edit: if you don't have Object.values available in your environment (it is experimental)
const originalQuestions = JSON.stringify(theJSONinYourQuestion);
const questionKeys = Object.keys(orginalQuestions);
const questionObjects = questionKeys.map(key => originalQuestions[key]);
...
You can maintain access to the question object inside of a .map by assigning it to a variable outside that scope. Here's a jsBin showing the idea
const objectOfQuestions = {
"questions": {
"q1": {
"qId": "1",
"type": "YN",
"text": "Text 1",
"tip": {
"logo": "assets/icons/gadgetz.svg",
"logofallback": "assets/img/gadgetz.png",
"heading": "Heading 1",
"text": "Text 2"
}
},
"q2": {
"qId": "2",
"type": "YN",
"text": "Text 1",
"tip": {
"logo": "assets/icons/gadgetz.svg",
"logofallback": "assets/img/gadgetz.png",
"heading": "Heading 1",
"text": "Text 2"
}
}
}
}
const Question = ({ id, question }) => {
return (
<div>
<h1>Question: {id}</h1>
<p>id: {question.qId}</p>
<p>type: {question.type}</p>
<p>text: {question.type}</p>
</div>
)
}
const QuestionList = ({ questions }) => {
const questionObj = questions;
return (
<div>
{Object.keys(questions.questions).map((key, i) => {
return (
<div key={key}>
<Question id={key} question={questionObj.questions[key]} />
</div>
);
})}
</div>
);
}
ReactDOM.render(<QuestionList questions={objectOfQuestions} />, document.getElementById('app'));
Related
I have a json file that contains content for several different pages that are under a "service" category. I use dynamic routes in nextJS by having a file as "[serviceId].tsx", this routing works. However I have a json file where I want to use the [serviceId] provided in the route to access information.
I have the following code in my [serviceId].tsx file:
const json = jsonFile.services
const router = useRouter()
const serviceId = router.query.serviceId
return (
<div>
<ArticleWithPicture title={content.title} description={content.description}/>
</div>
)
}
My json file looks similar to this (ive edited it to be more clear for this example):
{
"serviceId":
[
{
"service1": {
"id": "xx",
"title": "xxx",
"description": "xx",
"featuredCompany":
[
{ "id": "1",
"name": "xxx",
"companyPageURL": "/",
"imagePath": "xxx",
"description": "xxx",
"additionalServices": {
"service1": "xxx",
"service2": "xxx"
},
"instagramURL":"/",
"twitterURL": "/"
}
]
}
},
{
"service2": {
"id": "xxx",
"title": "xxx",
"description": "xxx",
"featuredCompany":
[
{ "id": "1",
"name": "xxx",
"companyPageURL": "/",
"imagePath": "xxx",
"description": "xxx",
"additionalServices": {
"service1": "xxx",
"service2": "xx"
},
"instagramURL":"/",
"twitterURL": "/"
}
]
}
}
]
}
Basically, each Service has the content for each indiviual page. So I want to dynamically set for instance the title of my component "ArticleWithPicture" based on the corresponding title in my json file based on the serviceId that I get from router.query.serviceId. However when I try the following code:
<ArticleWithPicture title={json.{serviceId}.title}/>
I get error (this is due to how I use "{}" within a "{}", is there a way to do this better?
But I also cannot access it if I do eg:
const title = json.serviceId.title
or (what is what I actually want to do ie: query the json file based on my serviceId provided by "router.query.serviceId")
const title = json.{serviceId}.title
I guess something might be wrong with either my json file structure or how I try to access it. Any advice would be appreciated.
Thanks!
I'm assuming the JSON you provided is your entire jsonFile.
If the JSON you provided is just jsonFile.services, then change any uses of jsonFile to jsonFile.services
and update the type.
The format of the JSON isn't great for your use case because there's a lot of unnecessary wrapping.
With your current JSON
Assuming you cannot modify the format of the JSON, you would have to find the service from the serviceId array:
function getService(json, serviceId) {
return json.serviceId
.find((serviceWrapper) => serviceWrapper[serviceId] !== undefined)
?.service;
}
A fully typed example:
type Service = {
id: string
title: string
description: string
// ...
};
// you don't have to use this, I just included it for clarity
type JsonFile = {
serviceId: {
[serviceId: string]: Service
}[]
};
function getService(json: JsonFile, serviceId: string): Service | undefined {
return json.serviceId
.find((serviceWrapper) => serviceWrapper[serviceId] !== undefined)
?.service;
}
// declare const jsonFile: JsonFile;
export default function ServicePage() {
const router = useRouter();
const serviceId = router.query.serviceId as string;
const content = getService(jsonFile, serviceId);
if (!content) return (
<div>
{'Article doesn\'t exist.'}
</div>
);
return (
<div>
<ArticleWithPicture title={content.title} description={content.description} />
</div>
);
}
With better JSON
An example JSON that would need less unwrapping is:
{
"service1": {
"id": "xx",
"title": "xxx",
// ...
},
"service2": {
"id": "xxx",
"title": "xxx",
// ...
}
}
type JsonFile = {
[serviceId: string]: Service
}
Then you would be able to just do jsonFile[serviceId] or jsonFile.services[serviceId] to get a service.
My First Application with ReactJS
I have no prior experience with reactjs and json, but am trying to increase my skill set and have been told reactjs is great for front end.
I am attempting to read an call an API, get a json object, parse it and display certain information. I am able to achieve almost all of this with relative ease with the help of several online tutorials, but I have hit a wall.
Issue:
I have parsed and displayed the majority of the json object but there is an array of strings in each object. I would like to take the average of that array from the json object and display that in html as well. Below is my attempt:
//importing necessary modules
import React from 'react';
import ReactDOM from 'react-dom';
//class creates the React component I am building
class ContentFeed extends React.Component {
constructor() {
super();
this.state = {
'workers': []
}
}
componentDidMount() {
this.getItems();
}
getItems() {
fetch('localhost:7575')
.then(results => results.json())
//.then(results => console.log(results));
.then(results => this.setState({'workers': results.workers}));
}
render() {
return (
<ul>
// I'm quite new to reactjs
// so I'm not too sure if what I'm doing below is legal syntax
{this.state.workers.map(function(item, index) {
var arrNum = 0;
var h = item.hours.split(",");
var sum = 0;
for(var i = 0; i < h.length; i++) {
sum += parseInt(h[i]);
}
arrNum = (sum/h.length);
return (
<div key={index}>
<h1>{item.firstName} {item.lastName}</h1>
<h4>{item.hireDate}</h4>
<h4>{item.dept}</h4>
<h4>arrNum</h4>
</div>
)
})}
</ul>
);
}
}
ReactDOM.render(
<ContentFeed/>,
document.getElementById('root')
);
What should appear is an html page with a header that contains the employees name followed by smaller headers containing the relevant employee info followed by an avg of their work hours.
However the webpage is blank.
I have checked the console tab in the developer's tools on the page and have seen an error like this:
Uncaught TypeError: item.hours.split is not a function
at index.js:29
at Array.map (<anonymous>)
at ContentFeed.render (index.js:26)
at finishClassComponent (react-dom.development.js:17160)
at updateClassComponent (react-dom.development.js:17110)
at beginWork (react-dom.development.js:18620)
at HTMLUnknownElement.callCallback (react-dom.development.js:188)
at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
at invokeGuardedCallback (react-dom.development.js:292)
at beginWork$1 (react-dom.development.js:23203)
I am unsure how to resolve this. I sincerely hope I have provided as much relevant information as needed. Thank you for any help.
UPDATE 1
This is from the console tab
Uncaught TypeError: item.hours.split is not a function
at index.js:29
at Array.map (<anonymous>)
at ContentFeed.render (index.js:26)
at finishClassComponent (react-dom.development.js:17160)
at updateClassComponent (react-dom.development.js:17110)
at beginWork (react-dom.development.js:18620)
at HTMLUnknownElement.callCallback (react-dom.development.js:188)
at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
at invokeGuardedCallback (react-dom.development.js:292)
at beginWork$1 (react-dom.development.js:23203)
UPDATE 2
This is some of the raw JSON from fetch
{
"workers": [
{
"firstName": "Tom",
"lastName": "Ronson",
"dept": "Sales",
"hireDate": "09/09/2015",
"hours": [
"8",
"10",
"4",
"6",
"6"
]
},
{
"firstName": "Bob",
"lastName": "Howser",
"dept": "Acct",
"hireDate": "01/05/2005",
"hours": [
"8",
"10",
"4",
"6",
"6"
]
},
{
"firstName": "Jane",
"lastName": "Winger",
"dept": "Legal",
"hireDate": "08/01/2008",
"hours": [
"5",
"6",
"5",
"5",
"6"
]
},
Your response data's hours property is an array of strings, not a comma separated list of hours as a string.
workers: [
{
firstName: "Tom",
lastName: "Ronson",
dept: "Sales",
hireDate: "09/09/2015",
hours: ["8", "10", "4", "6", "6"]
},
{
firstName: "Bob",
lastName: "Howser",
dept: "Acct",
hireDate: "01/05/2005",
hours: ["8", "10", "4", "6", "6"]
},
{
firstName: "Jane",
lastName: "Winger",
dept: "Legal",
hireDate: "08/01/2008",
hours: ["5", "6", "5", "5", "6"]
}
]
The average hours worked can be computed using an array::reduce to compute a sum of hours divided by the array length
const avg =
item.hours.reduce((sum, curr) => sum + Number(curr), 0) /
item.hours.length;
You can map the data as such (don't forget to use correct JSX syntax, i.e. <h4>{avg}</h4> versus <h4>avg</h4>)
{this.state.workers.map((item, index) => {
const avg =
item.hours.reduce((sum, curr) => sum + Number(curr), 0) /
item.hours.length;
return (
<li key={index}>
<h1>
{item.firstName} {item.lastName}
</h1>
<h4>{item.hireDate}</h4>
<h4>{item.dept}</h4>
<h4>{avg}</h4> // <-- * use correct JSX syntax
</li>
);
})}
there is an array of strings in each JSON object. I would like to take the average of that array
From your description, item.hours is an array of strings. But in your code
item.hours.split(",");
You treat it like a single, comma-separated, string.
Given you're getting TypeError, it seems it is in fact an array of strings (preferably you'd update your question with sample data).
To get the average of an array, you can use reduce:
const avg = item.hours.reduce((a,b) => a + parseFloat(b),0) / item.hours.length;
I am trying to list the server response , but some mistake is their in my code about accessing nested json..Following is the structure of json
Updated:
{
"child": [],
"courses": [{
"data": {
"name": "Student 1",
"date_created": 1514610451,
"total_students": 4,
"seats": "",
"start_date": false,
"categories": [{
"name": "Subject",
"slug": "Subject"
}],
"intro": {
"id": "1",
"name": "Main Admin",
"sub": ""
},
"menu_order": 0
},
"headers": [],
"status": 200
}]
}
And my react part is
render(){
return this.state.course.map(course =>
<Text style={styles.userStyle}>{course.courses.data.map(datas => datas.name)}</Text>
);
}
Please help me to figure out the mistake.I am getting this.state.course.map is not a function.My fetch request is as follows
state= {course:[]};
componentWillMount(){
fetch('https://www.mywebsite.com/' + this.props.navigation.state.params.id)
.then((response) => response.json())
.then((responseData) => this.setState({course: responseData}))
}
So you would need to show us how this.state is set, but if you're doing something like this.setState(jsonObject), the property you are looking for seems to be this.state.courses. This would access the array of courses. However, in the subsequent lines you try to access course.courses, which suggests you're setting the state like this.seState({course: jsonObject}) so it's not clear.
I'd say if you fix the first problem, you'll immediately hit another one because it doesn't look like data is an array but an object, so trying to call map on it is unlikely to do what you want (unless you've been playing with prototypes).
EDIT:
In response to the new info, I recommend the following:
render(){
if(this.state.course && this.state.course.courses) {
return this.state.course.courses.map(course =>
<Text style={styles.userStyle}>{course.data.name}</Text>
);
} else {
return [];
}
}
Im building a React app and I have a quite complex JSON file where I need to find and output certain values of an object in an array.
Im trying to output all my people from my JSON, they look something like this:
people: [
{
"id": 1,
"email": "Sincere#april.biz",
"address": [
{
"street": "Kulas Light",
"type": "house",
"attribute": {
"sketch": "sketch.jpg",
"photo": "photo.jpg"
}
},
{
"street": "Lorem Ipsum",
"type": "apartment",
"attribute": {
"sketch": "sketch.jpg",
"photo": "photo.jpg"
}
}
]
}
]
I have no problem to output the email, doing it like so:
var App = React.createClass({
getInitialState: function () {
return {
results: {}
}
},
componentDidMount() {
fetch(REQUEST_URL) // fetch from API, returns JSON
.then(res => res.json())
.then(data => {this.setState(
{ results: data.people}
);
})
},
renderResult : function(key){
return <Result key={key} index={key} details={this.state.results[key]}/>
},
render : function() {
return (
<ul>
{Object.keys(this.state.results).map(this.renderResult)}
</ul>
)
}
});
var Result = React.createClass({
render : function() {
return (
<li>
{this.props.details.email}
<img src="{this.props.details.address.type=house.attribute.photo}"/>
</li>
)
}
});
ReactDOM.render(App, document.querySelector('#app'));
However, now I need to output "photo" but only for "type": "house". I tried this but no luck, well aware that this is way off. Im quite new to handling JSON data and React and Google hasn't helped me even after a few hours of trying to solve this.
The .address property isn't an object but an array of objects so
.type is not available directly on .address:
this.state.results.people.address.type
// .type property doesn't exist on array
Solution:
You can use Array.prototype.filter on .address to obtain an array of objects that have a property type whose value is "house":
var houseAddresses = this.state.results.people.address.filter(function(value){
return value.type === "house";
});
Here, houseAddress will be an array of objects whose type value is 'house".
You can then loop through the array to create the relevant JSX using for, Array#forEach or Array#map. The following example uses Array#map:
const houseImgTags = houseAddresses.map(function(house, index){
return (
<img
src={house.attribute.photo}
key={'house'+index}
/>
);
});
(A key was added here in case there are more than one instance of a house object)
You can simply write.
<img src={this.states.results.address.type==="house"?house.attribute.photo : otherwise_photo}/>
Basically this would compare address.type is house or not,then return the result corresponded.
I am new to react. I am using an api in the form of
{
"category": "dogs",
"available": [
{
"name": "pedro",
"breed": "chihuahua"
},
{
"name": "roxane"
"breed": "beagle"
}
]
},
{
"category": "cat",
"available": [
{
"name": "garfield",
"breed": "tobby"
}
]
}
I want to display ALL the pets available from the same category in name-breed pairs.
Example: Display all dogs
Pedro
Chihuahua
Roxane
Beagle
but that array is giving me hard times.
Attempt
App.jsx
{this.state.data.map((availablePets, i) => <Content key = {i} data = {Content} />)}
Content.jsx
{this.props.data.available[WhatDoIPutHere?].name}
{this.props.data.available[ajsnxnnx].breed}
Is there another way to display ALL the available pets from the same category in name-breed pairs?
SOLVED
{this.props.data.faqs.map((QA,i) =>
<div key={i}>
<h4>{this.props.data.category[i].name}</h4>
<p>{this.props.data.category[i].breed}</p>
</div>
)}
Your solution is not dynamic.
This will be a better solution.
{
this.props.data.faqs.map((QA,i) => {
const pets = QA.available.map((pets, j) => {
return <div key={j}
<h4>{pets.breed}</h4>
<p>{pets.name}</p>
</div>
})
return <div key={i}>
<h4>{QA.category}</h4>
{pets}
</div>
})
}