React reading nested JSON data fails when loads or refresh - reactjs

I am developing a screen to show data based on search results. Below is the JSON output.
When I click search result below component is called. In the result screen i need id, name and table1, table2 data.
Table1 and Table2 are nested outputs and these will be displayed in React Table or Data Grid (next step)
Issue: Unable to render StudyData.table1.name
Options Tried
1. UseEffect()
UseEffect() able to read StudyData.studyname but not StudyData.table1.name
Assigned to a variable
Assigned to a state
2. Render
tried using map for subdocuments
Finding: It fails only first time load and refresh. I tried to comment -> save->load -> remove the comments and save. Then works (as component is loaded). I am missing something during the first time load or refresh. Please help
[
{
"id": "DD3",
"studydate": "DDD",
"studydescription": "DD3 Description",
"studyname": "DD3",
"table1": [
{
"no": "1",
"name": "DD3 Name",
"date": "Krishna",
"description\r": "1111\r"
},
{
"no": "2",
"name": "DD3 Nam2",
"date": "Test2",
"description\r": "2222\r"
},
{
"no": "3",
"name": "DD3 Name3",
"date": "Test3",
"description\r": "3333"
}
],
"table2": [
{
"No": "2",
"Study Field1": "21",
"Study Field2": "22",
"Study Field3\r": "23"
}
],
"table3": [
{
"No": "3",
"Study Field5": "T31",
"Study Field6": "T32",
"Study Field7": "T33",
"Study Field 8\r": "T34"
}
],
"_rid": "QeNcANZFTTIKAAAAAAAAAA==",
"_self": "dbs/QeNcAA==/colls/QeNcANZFTTI=/docs/QeNcANZFTTIKAAAAAAAAAA==/",
"_etag": "\"33002e92-0000-0200-0000-5fa6fe320000\"",
"_attachments": "attachments/",
"_ts": 1604779570
}
]
COMPONENT CODE
import React, { useEffect } from "react";
import api from "./UploadStudyFilesapi";
import { DataGrid } from "#material-ui/data-grid";
export default function StudyDisplay(props) {
let myData = props.Study;
const [StudyData, setStudyData] = React.useState([]);
const [Table1Rows, setTable1Rows] = React.useState([]);
useEffect(() => {
// Update the document title using the browser API
api.getStudy(myData.studyname).then((json) => setStudyData(json));
console.log(StudyData.studyname); //works
//console.log(StudyData.table1.no) //doesn't work
let myTable = StudyData.table1;
console.log(myTable);
setTable1Rows(StudyData.table1);
console.log(Table1Rows);
}, [myData]);
return (
<div>
{StudyData.length === 0 ? (
<h1>Loading...</h1>
) : (
StudyData.map((item) => (
<div key={item.studyname}>
{item.studyname}
{/* DOESNT WORK first brose or refresh*/}
{item.table1.map((key2) => (
<div>
{console.log(key2.name)}
{key2.name}
{/* Want to pass Key2 to DataGrid or React-Table */}
{/* <DataGrid rows={key2} columns={{field1:"No"}} /> */}
</div>
))}
</div>
))
)}
</div>
);
}

Thanks for your support. I added conditional rendering based on search selection in parent component. That resolved all the issues

Related

How do I iterate, dynamically load my form input elements and then retrieve the input values on Form Submit in React?

I am creating a sample dynamic form and I want to load my input elements based on a JSON which contains different input elements like "textbox", "text-area", "dropdown", "radio-input" and so on..
I have a JSON file created to get this as shown below:
[
{
"id": "1",
"type": "textbox",
"text": "",
"required": true,
"label": "lbl"
},
{
"id": "2",
"type": "textbox",
"text": "",
"required": true,
"label": "Specification Name"
},
{
"id": "3",
"type": "dropdown",
"text": "",
"required": true,
"label": "Specification Reviewed",
"options":["a","2"]
},
{
"id": "4",
"type": "dropdown",
"text": "",
"required": true,
"label": "Action Required",
"options":["1","2","3"]
},
{
"id": "5",
"type": "textbox",
"text": "",
"required": true,
"label": "lbl"
}
]
I have an App base component which calls another component called "Input" which has my layout and I retrieve the elements through that component. I am able to pull the text box and dropdown here but I am not able to iterate through the dropdown select. I'm not sure how to do it.
Here's my App Base solution: Here I use the map concept to fetch the data from the JSON local file and assign it to inputvalues which I then use in the return within the form tag.
I'm able to list all my input elements dynamically
But I'm not able to get the dropdown values from my JSON file
function App() {
const [inputObject, setInputObject] = React.useState(inputData)
const inputvalues = inputObject.map( input => {
return (
<Input
key={input.id}
input={input}
/>
)
})
const handleSubmit = (event) => {
event.preventDefault();
}
return (
<div className="App">
<header className="App-header">
<form>
<div>
{inputvalues}
</div>
<input type="submit" value="submit" onClick={handleSubmit} />
</form>
</header>
</div>
);
}
export default App;
And, here's my input.js component file: This basically lays out the input elements and I fetch the data using Props but I am unable to fetch the dropdown selection values because I would need to somehow iterate within each of those dropdown elements.
export default function Input(props) {
const [state, setState] = React.useState({
textBoxValue: ""
})
function handleChange(evt) {
setState({ [props.input.id] : evt.target.value });
}
if (props.onChange) {
props.onChange(state);
}
return (
<div>
<label>{props.input.type}: </label>
{props.input.type === "textbox" && <input name={props.input.type} placeholder={props.input.type} id={props.input.id} value={state.firstName} onChange={handleChange}/>}
{props.input.type === "dropdown" && <select name={props.input.type} id={props.input.id}>
<option value={props.input.options}></option></select>
}</div>)}
Please help me or guide me because I'm still learning React.
In addition to this, how would i later get all the input values upon FORM SUBMIT ? For this I tried adding a handleChange event to see if data comes through but it does not work.
Thank you so much in advance!
You may find Yup and Formik useful.
With Yup, you can include types to fields as well as things such as if the field is required.
The example linked should get you in the right direction.
Edit - (after OP comment)
So without using any external library, you could do something like this:
// Get a hook function
const {useState} = React;
const INPUTS = [
{
"id": "1",
"type": "textbox",
"value": "",
"required": true,
"label": "lbl"
},
{
"id": "2",
"type": "textbox",
"value": "",
"required": true,
"label": "Specification Name"
},
{
"id": "3",
"type": "dropdown",
"value": "",
"required": true,
"label": "Specification Reviewed",
"options":["a","2"]
},
{
"id": "4",
"type": "dropdown",
"value": "",
"required": true,
"label": "Action Required",
"options":["1","2","3"]
},
{
"id": "5",
"type": "textbox",
"value": "",
"required": true,
"label": "lbl"
}
];
const convertArrayToObject = (array, key, targetKey) => {
const initialValue = {};
return array.reduce((obj, item) => {
return {
...obj,
[item[key]]: item[targetKey],
};
}, initialValue);
};
const Form = () => {
const [formState, setFormState] = useState(
convertArrayToObject(INPUTS, "id", "value")
);
const handleChanges = (keyName) => (e) => {
const newValue = e.target.value
setFormState(prev => ({
...prev,
[keyName]: newValue
}));
}
console.log(formState);
return (
<form>
{INPUTS.map((input, inputIndex) => (
<div key={inputIndex}>
<label htmlFor={input.id}>{input.label}</label>
{input.type === "dropdown" && input.options ? (
<select onChange={handleChanges(input.id)}>
{input.options.map((option, optionIndex) => (
<option
key={optionIndex}
value={option}>{option}
</option>
))}
</select>
) : (
<input
id={input.id}
name={input.id}
required={input.required}
type={input.type}
onChange={handleChanges(input.id)}
value={formState.value}
/>
)}
</div>
))}
</form>
);
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Form />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
A little reasoning behind some of the code written:
React wants a key prop to be passed when mapping over objects (hence I've added it for each wrapper div and option element.
I've mapped over the INPUTS object to build the initial state, and then created an onChange handler that is curried, that way it is generic enough to be used everywhere
I'm just console.loging the formState to demonstrate the changes as you update the form.
Beyond
Consider adding Auto Complete if applicable or worthwhile
You will of course need some kind of some kind of submit button if you plan to submit the data to an API.
<button type="submit">Submit</button
But I will leave that as an exercise for you...
Hope this helps!

Building quiz site with react, I have 4 buttons that's an option of the question answer

I mapped the component which generated 5 questions and obviously 20 buttons. My problem now is that the first 4 buttons on each row always have the same ID when I try to get each button clicked so I can check if the correct answer was selected. I need every button to have a unique ID. I've tried different ID functions but none seem to work, I think the problem is from my logic and not the ID itself.
import React from "react";
export default function Question_page(props) {
const [triviaApi, setTriviaApi] = React.useState()
const styles = {
backgroundColor: props.isHeld ? "#59E391" : "grey"
}
return (
<div className="main-question-container">
<div className="question">
<h3>{props.my_items.question}</h3>
</div>
<div className="question-button">
<button onClick={props.holdAnswer}>{props.my_items.incorrect_answers[0]}</button>
<button onClick={props.holdAnswer}> {props.my_items.incorrect_answers[1]}</button>
<button onClick={props.holdAnswer}>{props.my_items.correct_answer}</button>
<button onClick={props.holdAnswer}>{props.my_items.incorrect_answers[2]}</button>
</div>
</div>
)
}
The App components below
import QuestionPage from "./components/Question_page";
import React from "react";
import {nanoid} from 'nanoid'
import {BrowserRouter as Router, Route, Routes} from "react-router-dom"
import JsonData from "./JsonData";
export default function App() {
const [colorState, setColorState] = React.useState({
isClicked: true
})
const jsonDataElement = JsonData.map(item => {
return item.results.map((innerItem,index) => {
const myID = Math.random()
return (
<QuestionPage
key={myID}
my_items={innerItem}
isHeld={innerItem.isClicked}
holdAnswer={() => holdAnswer(myID)}
/>
)
})
})
function holdAnswer(ID) {
// here is where I want to check if every bottom has a different ID so I can change the colour and save the variable to check if the answer was right during my "check answer" click. The issue is, that the first button on the row gives me the same ID, I need to get a different ID on every button which contains the answer option so I can run this function
console.log(ID)
}
return (
<Router>
<NavBar/>
<Routes>
<Route path="/" element={<Start/>}/>
<Route path="/question" element={
<div>
{jsonDataElement}
<div className="check-answer-container">
<button className="check-answer">Check answer</button>
</div>
<hr/>
</div>
}/>}/>
</Routes>
</Router>
);
}
The JSON data im using
export default [{
"response_code": 0,
"results": [
{
"category": "Science: Computers",
"type": "multiple",
"difficulty": "medium",
"question": "Which of these is the name for the failed key escrow device introduced by the National Security Agency in 1993?",
"correct_answer": "Clipper Chip",
"incorrect_answers": [
"Enigma Machine",
"Skipjack",
"Nautilus"
]
},
{
"category": "Science: Computers",
"type": "multiple",
"difficulty": "medium",
"question": "In the server hosting industry IaaS stands for...",
"correct_answer": "Infrastructure as a Service",
"incorrect_answers": [
"Internet as a Service",
"Internet and a Server",
"Infrastructure as a Server"
]
},
{
"category": "Science: Computers",
"type": "multiple",
"difficulty": "medium",
"question": ".rs is the top-level domain for what country?",
"correct_answer": "Serbia",
"incorrect_answers": [
"Romania",
"Russia",
"Rwanda"
]
},
{
"category": "Science: Computers",
"type": "multiple",
"difficulty": "medium",
"question": "What was the first Android version specifically optimized for tablets?",
"correct_answer": "Honeycomb",
"incorrect_answers": [
"Eclair",
"Froyo",
"Marshmellow"
]
},
{
"category": "Science: Computers",
"type": "multiple",
"difficulty": "medium",
"question": "The name of technology company HP stands for what?",
"correct_answer": "Hewlett-Packard",
"incorrect_answers": [
"Howard Packmann",
"Husker-Pollosk",
"Hellman-Pohl"
]
}
]
}]
You're only setting myID once right now so all buttons are receiving the same ID - you need to set it for each button instead of once for the whole QuestionPage.
You could probably make a function on Question_page that returns Math.random and then call that from id={} on each button

How do I iterate over a JSON file in react?

I'm new to React and for the life of me cannot figure this out.
I have a JSON file (Fontawesome icons):
{
"icons": [
{
"name": "Glass",
"id": "glass",
"unicode": "f000",
"created": 1,
"filter": [
"martini",
"drink",
"bar",
"alcohol",
"liquor"
],
"categories": [
"Web Application Icons"
]
},
{
"name": "Music",
"id": "music",
"unicode": "f001",
"created": 1,
"filter": [
"note",
"sound"
],
"categories": [
"Web Application Icons"
]
},
// etc
To start with I just want to return the name of each icon.
I've been trying to follow various tutorials and have:
import React, { PureComponent } from "react";
import iconList from './services/iconList';
export default class App extends PureComponent {
render() {
const items = iconList.map(data=>{
return(
<div>
<ul>
<li>
<span>{data.name}</span>
</li>
</ul>
</div>
)
})
return items;
}
}
But I get the error: .map is not a function.
I'm not sure what I can do differently. Each tutorial I see seems to use the map function. Is there a better/different way?
Try using
const items = iconList.icons.map(data=>{
Your data is an object with an icons property in it. You can also destructure your iconList when you import:
import {icons as iconList } from './services/iconList';

Right way to parse Redux state props - using super json objects

Following my trip with React and Redux, I'm facing a problem, simple in appearance, but hard to solve : I'm setting my Redux state with a very big JSON object. Those datas are retrieved from an async call. When I'm setting it, I created an entry in my reducer with
let initialState = {
pages: []
}
Then, I'm putting different pages, with their params and datas into this pagesarray. So far so good, the state is well updated.
BUT, my different app pages use only parts of it as you can imagine. For instance, I have a page named Gallery which might need my state to look like this :
"pages": [
{
"component":"Gallery",
"key": "gallery",
"title": "Galerie photos",
"url": "/galerie-photos",
"sections": [
{
"component": "Gallery",
"params": {
"images": [
{
"id": 1,
"src": "http://lorempicsum.com/futurama/627/300/1",
"alt": "alternate text"
},
{
"id": 2,
"src": "http://lorempicsum.com/futurama/500/400/2",
"alt": "alternate text"
},
{
"id": 3,
"src": "http://lorempicsum.com/futurama/400/320/3",
"alt": "alternate text"
},
{
"id": 4,
"src": "http://lorempicsum.com/futurama/800/500/4",
"alt": "alternate text"
},
{
"id": 5,
"src": "http://lorempicsum.com/futurama/320/300/5",
"alt": "alternate text"
},
{
"id": 6,
"src": "http://lorempicsum.com/futurama/420/360/6",
"alt": "alternate text"
}
]
}
}
]
}
]
In my GalleryContainer, I'm requesting only the images property as this is the only concern of my Gallery component. I'm able to retrieve this data with no problem. Testing with the following code returns the desired images array :
But as far as I tested this, the images are not retrieved by my Gallerycomponent : when console logging on the component side, I got undefined, and on the container side, no problem.
I tested something different : I set a galleryImages property via the reducer, and set it with the gallery images directly. This worked.
The questions are : Why doesn't it work in the first case, and do in the second ? Do I have to work only with datas that are set as state properties only ? Can't I have a "super" json set in my state to work with directly ?
Thanks to light me up on this one :)
// Component
import React from 'react'
const Gallery = ({images}) => (
<div>
{images.map((image, key) =>
<div key={key} className="image-element-class" style={card}>
<img src={image.src} alt={image.alt} style={imageStyle}/>
</div>
)}
</div>
)
const card = {
width: 'calc((100% / 3) - (15px / 1.5))',
marginBottom: '15px',
boxSizing: 'border-box'
}
const imageStyle = {
width: '100%'
}
export default Gallery
// Container
import { connect } from 'react-redux'
import Gallery from '../../components/pages/Gallery'
const getImages = state => {
return {
images: state.data.pages.filter(page => page.component === 'Gallery' &&(
page.sections.filter(section => section.component === 'Gallery' &&(
section.params.images
))
)),
}
}
const mapStateToProps = state => {
return getImages(state)
}
const GalleryContainer = connect(
mapStateToProps,
{}
)(Gallery)
export default GalleryContainer
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>

How can you display an specific number of rendering after mapping a json object?

This is my json object.
"properties":{
"summary":[
{
"id":0,
"name": "John Doe",
"address": "Privet Drive",
"sex":"M"
},
{
"id":1,
"name": "John Smith",
"address": "Elm Street",
"sex":"M"
},
{
"id":2,
"name": "Jane Doe",
"address": "Privet Drive B",
"sex":"F"
},
{
"id":3,
"name": "Peter Parker",
"address": "Privet Drive C",
"sex":"M"
},
{
"id":4,
"name": "Harry Potter",
"address": "Privet Drive D",
"sex":"M"
}
]
}
This is the on my react file
class Thumbnail extends React.Component{
render(){
const datax = this.props.info;
return (
<div>
{
datax.map(function(obj){
{
let summary = obj.properties.summary;
let summaryLength = summary.length;
return (
summary.map(function(item){
return <div className={"col-md-3 col-sm-6"} key={item.id}>
<p>
<a href={'http://localhost:8080/'} className="listing-link">
<PersonName name={item.name} />
</a>
</p>
<PersonAddress address={item.address} />
<PersonSex address={item.sex} />
</div>;
})
)
}
})
}
</div>
)
}
}
The ReactDom.render is on a different file which I can access by this:
ReactDOM.render(<Thumbnail info={data} />, document.getElementById('featured-list'));
I want to display only 4 sets of properties from the props object. This code displays 5 of them based on the props which has 5 sets.
I tried doing a iteration inside the map function but it fails.
How can I achieve this?
You can use slice to display only first four sets. You can try like this :
summary.slice(0,4).map(function(item){ .... })
That will return only first four sets. Here is a fiddle
Another solution
With the iteration inside the map function, you can do it like this :
{summary.map((s, i) => i <= 3 ? <div key={i}>{s.name}</div> : "")}
That will also return only first four sets. Here is a fiddle.
Hope this helps.

Resources