Mapping JSON data with React Hooks - json

I'm working on a small project and I am trying to map data from a JSON file into my project.
In components with nested data, I keep getting an let data = props.data["runways"];.
data.json:
{
"runways":[
{
"slot":"Area 1",
"planes":[
{
"name":"PanAm",
"number":"12345",
"start":{
"time":1585129140
},
"end":{
"time":1585130100
}
},
{
"name":"PanAm 222 ",
"number":"12345",
"start":{
"time":1585129140
},
"end":{
"time":1585130100
}
}
]
}
]
}
App.js,
I pass the JSON data as props:
import planeData from './plane_info.json'
const Container = () => {
const [planeDataState, setPlaneDataState] = useState({})
const planeData = () => setPlaneDataState(planeData[0].runways)
return (
<>
<MyPlane planeInfo={planeDataState}/>
<button onClick={planeData} type="button">Get Data</button>
</>
)
}
and finally, I want to bring my data into my component:
MyPlane.jsx
const MyPlane = (props) => {
let data = props.data["runways"];
if(data)
console.log(data, 'aaa')
return (
<>
{
data ? (
<div>
<span>{props.planeInfo.name}</span>
<span>RAIL TYPE: {props.planeInfo.type}</span>
</div>
) : <h6>Empty</h6>
}
</>
);
}
According to the error message, the problem occurs at this line of code: let data = props.data["runways"]; However, I believe that I am passing the data for runways from the JSON file.
I've never worked with React Hooks to pass data, so I'm confused about why this error is occurring.

In order to map effectively over the JSON data it's necessary to understand how that data structure is composed.
If you're unsure, using JSON.stringify() is a great way to get the "bigger picture" and then decide what exactly is it that you want to display or pass down as props to other components.
It appears you wish to get the plane data (which is currently an array of 2 planes). If so, you could first get that array, set the state, then map over it to display relevant info. Perhaps like this:
const data = {
"runways":[
{
"slot":"Area 1",
"planes":[
{
"name":"PanAm",
"number":"12345",
"start":{
"time":1585129140
},
"end":{
"time":1585130100
}
},
{
"name":"PanAm 222 ",
"number":"12345",
"start":{
"time":1585129140
},
"end":{
"time":1585130100
}
}
]
}
]
}
function App() {
const [ planeData, setPlaneData ] = React.useState(null)
React.useEffect(() => {
setPlaneData(data.runways[0].planes)
}, [])
return (
<div className="App">
{/* {JSON.stringify(planeData)} */}
{planeData && planeData.map(p => (
<p key={p.name}>
{p.name} | {p.number} | {p.start.time} | {p.end.time}
</p>
))}
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

Here const planeData = () => setPlaneDataState(planeData[0].runways)
In this line, planeData[0].runways will be undefined according to the json file which you have shared.
Instead try setting and passing entire json object, ie,
const planeData = () => setPlaneDataState(planeData)
Try this, And then inside MyPlane.jsx component, let data = props.data["runways"]; this won't be undefined. So , the error won't come.

At the beginning there is no data in props.data['runways'] (also you can use props.data.runways, I guess you come from another language like Python as of this syntax that you are using), because you sent the request at first, it takes time for request to be satisfied, so you need to check in your <MyPlane /> component to see if there is a runways key in data and then proceed to render the component, something like below:
const MyPlane = (props) => {
const data = props.data
return (
<>
{
data.runways
? <>
...your render able items that you wrote before
</>
: <p>There is no data yet!</p>
}
</>
)
}
Also please note that you might return something from component. At your case your render is inside the if(data){...} statement! what if the condition was not satisfied? which is your current error case !
NOTE: please check that you are passing your planeDataState as planeInfo prop to the child component, so you might have something like:
const data = props.planInfo
to be able to use the data variable that you've defined before the render part.

Related

json file missing Brackets

json format test with postman
{
"localTimeStamp": "2021-08-14T08:19:17.000Z",
"ipAddress": "10.0.5.26",
"subnetMask": "N/A",
"defaultGateway": "N/A",
"productType": "UNIVERGE BX9000",
"versionID": "7.20A.256.721",
"protocolType": "SIP",
"operationalState": "UNLOCKED",
"highAvailability": "Not Operational",
"serialNumber": "9107130",
"macAddress": "00908f8af6ba",
"systemUpTime": 4049040,
"saveNeeded": false,
"resetNeeded": false
}
i use this code in nex.js to fetch the info
import styles from '../styles/Jobs.module.css'
export const getStaticProps = async () => {
const res = await fetch('http://10.0.5.26/api/v1/status',{
headers: { Authorization: "Basic passsssworrdssss" }
}
);
const data = await res.json();
return {
props: { ninjas: data }
}
}
const Ninjas = ({ ninjas }) => {
console.log(ninjas)
return (
<div>
<h1>All Ninjas</h1>
{ninjas.map(ninja => (
<div key={ninja.id}>
<a className={styles.single}>
<h3>{ ninja.productType }</h3>
</a>
</div>
))}
</div>
);
}
export default Ninjas;
the error is browser
enter image description here
think the problem is The .map function is only available on array.
It looks like data isn't in the format you are expecting it to be (it is {} but im expecting []).
hopefully someone knows the solution to solve the error
output of console.log(ninjas) looks ok now
only got still error in browser
ReferenceError: ninja is not defined
[
{
localTimeStamp: '2021-08-14T10:30:35.000Z',
ipAddress: '10.0.5.26',
subnetMask: 'N/A',
defaultGateway: 'N/A',
productType: 'UNIVERGE BX9000',
versionID: '7.20A.256.721',
protocolType: 'SIP',
operationalState: 'UNLOCKED',
highAvailability: 'Not Operational',
serialNumber: '9107130',
macAddress: '00908f8af6ba',
systemUpTime: 4056919,
saveNeeded: false,
resetNeeded: false
}
]
It's an object so you don't need to loop through it to get the value insides:
Just direct access them like this:
<div>
<h1>All Ninjas</h1>
<div key={ninja.id}>
<a className={styles.single}>
<h3>{ ninja.productType }</h3>
</a>
</div>
</div>
- Be aware that id is not available in your object so ninja.id is invalid which return an undefined
- Consider using getServerSideProps to fetch data on each request. Because getStaticProps fetch data at build time
props: { ninjas: data }
Here, according to your description, data is given as
{ "localTimeStamp": "2021-08-14T08:19:17.000Z" ...}
So ninjas is not an array, it's a normal object now.
ninjas.map requires ninjas to be an array and as it is not, an error occured.
To fix it, you can fix this part.
props: { ninjas: [data]}

Property does not exist on type 'never' on JSON array

I'm just trying to fetch some JSON data from a url. The JSON data is formatted like so (reduced to two entries for simplicity):
[
{
"id": 1
"name": "Brett",
"gender": "male"
},
{
"id": 2
"name": "Sandra",
"gender": "female"
}
]
I can print profiles using console.log(profiles) and see all the entries in the console, but when i try to access the .name field i get the error
Property 'name' does not exist on type 'never'.
Here is the code for the app:
const URL = 'someurl'
function App() {
const [curId, setId] = useState(0);
//const [curProfile, setCurProfile] = useState(undefined);
const [profiles, setProfiles] = useState([])
useEffect(() => {
fetch(URL)
.then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error("Something went wrong!");
}
})
.then(
(response) => {
setProfiles(response);
setId(1);
//setCurProfile(profiles[curId - 1]);
})
.catch((error) => {
console.log(error)
})
}, []);
return (
<div className="App">
<p>
{profiles[curId].name}
</p>
</div>
);
}
export default App;
Also as a side question, I'm having some problems storing the current profile in the curProfile variable. Could someone point me in the right direction for that? Thanks!
The initial state of profiles is empty array and curId is 0, so profiles[curId] should be undefined thus profiles[curId].name would be error as initial rendering.
You should always check if profiles is empty or not.
return (
<div className="App">
{profiles.length > 0 &&
<p>
{profiles[curId].name}
</p>
}
</div>
)
You've got to type your state, otherwise Typescript won't know what to expect. You also need to type the response.
Something like:
type Profile = {
id: number,
name: string,
gender: string
}
const [profiles, setProfiles] = useState <Profile[]> ([]);
(...)
setProfiles(response as Profile[]);

How to access ag-Grid API in React function component (useState hook)?

What is the best way of accessing ag-Grid API inside of React function component?
I have to use some of the methods from API (getSelectedNodes, setColumnDefs etc.) so I save a reference to the API (using useState hook) in onGridReady event handler:
onGridReady={params => {
setGridApi(params.api);
}}
and then I can call the API like this: gridApi.getSelectedNodes()
I haven't noticed any problems with this approach, but I'm wondering if there's more idiomatic way?
Stack:
ag-grid-community & ag-grid-react 22.1.1
react 16.12.0
We find the most idiomatic way to use a ref. As the api is not a state of our component. It is actually possible to simply do:
<AgGridReact ref={grid}/>
and then use it with
grid.current.api
Here an example:
import React, { useRef } from 'react'
import { AgGridReact } from 'ag-grid-react'
import { AgGridReact as AgGridReactType } from 'ag-grid-react/lib/agGridReact'
const ShopList = () => {
const grid = useRef<AgGridReactType>(null)
...
return (
<AgGridReact ref={grid} columnDefs={columnDefs} rowData={shops} />
)
}
The good thing here is, that you will have access to the gridApi but als to to the columnApi. Simply like this:
// rendering menu to show/hide columns:
{columnDefs.map(columnDef =>
<>
<input
type='checkbox'
checked={
grid.current
? grid.current.columnApi.getColumn(columnDef.field).isVisible()
: !(columnDef as { hide: boolean }).hide
}
onChange={() => {
if (grid.current?.api) {
const col = grid.current.columnApi.getColumn(columnDef.field)
grid.current.columnApi.setColumnVisible(columnDef.field, !col.isVisible())
grid.current.api.sizeColumnsToFit()
setForceUpdate(x => ++x)
}
}}
/>
<span>{columnDef.headerName}</span>
</>
)}
Well I am doing it in my project. You can use useRef hook to store gridApi.
const gridApi = useRef();
const onGridReady = params => {
gridApi.current = params.api; // <== this is how you save it
const datasource = getServerDataSource(
gridApi.current,
{
size: AppConstants.PAGE_SIZE,
url: baseUrl,
defaultFilter: props.defaultFilter
}
);
gridApi.current.setServerSideDatasource(datasource); // <== this is how you use it
};
I'm running into the same issue but here is a workaround that at least can get you the selected rows. Essentially what I'm doing is sending the api from the agGrid callbacks to another function. Specifically I use OnSelectionChanged callback to grab the current row node. Example below:
const onSelectionChanged = params => {
setDetails(params.api.getSelectedRows());
};
return (<AgGridReact
columnDefs={agData.columnDefs}
rowSelection={'single'}
enableCellTextSelection={true}
defaultColDef={{
resizable: true,
}}
rowHeight={50}
rowData={agData.rowData}
onCellFocused={function(params) {
if (params.rowIndex != null) {
let nNode = params.api.getDisplayedRowAtIndex(params.rowIndex);
nNode.setSelected(true, true);
}
}}
onSelectionChanged={function(params) {
onSelectionChanged(params);
params.api.sizeColumnsToFit();
}}
onGridReady={function(params) {
let gridApi = params.api;
gridApi.sizeColumnsToFit();
}}
deltaRowDataMode={true}
getRowNodeId={function(data) {
return data.id;
}}
/>);

Trying to use local-storage in react app, however not working in between pages

I'm trying to save an array to local-storage in my react app, so that if the user goes to another page in the app, or closes the app and reopens it, the value stays the same.
In my index.js (simplified code):
import ls from 'local-storage';
function HomeIndex() {
const [testString, setTestString] = useState(ls('localStorageText') || '');
if(condition){
const array = [1,2,3];
const saveArray = {key: array};
localStorage.setItem('key1', JSON.stringify(saveArray));
const restoreValue = localStorage.getItem('key1');
setTestString(JSON.parse(restoreValue).key);
}
return (
<div className="col-12">
{testString}
</div>
);
}
When I press the button, and the condition is met, the testString value displays 123 as it should. And it holds the value. However it does not work when I try and add my own array.
const array = reversedHistoryText;
const saveArray = {key: array};
localStorage.setItem('key1', JSON.stringify(saveArray));
const restoreValue = localStorage.getItem('key1');
setTestString(JSON.parse(restoreValue).key);
It doesn't display anything the first time the button is clicked, then gives error on the 2nd time:
Error: Minified React error #31;
When I do this test:
setTestString(JSON.stringify(reversedHistoryText));
The result is []
You need to set your testString to the localStorage value.
import ls from "local-storage";
import React, { useEffect, useState } from "react";
function MyComponent() {
const [testArray, setTestArray] = useState([]);
useEffect(() => {
setTestArray(ls("testArray") || []);
}, []);
function handleClick(e) {
ls("testArray", [
{ id: 1, name: "this" },
{ id: 2, name: "thing" },
{ id: 3, name: "is" },
{ id: 4, name: "cool" }
]);
setTestArray(ls("testArray"));
}
return (
<div className="col-12">
<ul>
{testArray.map(obj => (
<p key={obj.id}>{obj.name}</p>
))}
</ul>
<button onClick={handleClick}>Set The State</button>
</div>
);
}
export default MyComponent;
You don't need to use third party for localStorage.
Just use localStorage without importing anything.
To save,
localStorage.setItem('key', 'value');
To get value from localStorage,
localStorage.getItem('key') // value
To remove value,
localStorage.removeItem('key')
Use
if(condition) ls('localStorageText', "TEST");
setTestString(ls('localStorageText')|| ' '); }
Instead
if(condition){ ls('localStorageText', "TEST");
setTestString(ls('localStorageText')); }
Because when you go back to the index page a new instance of this component is rendered and i think the condition in the if statement is false, so the code don't change the setstate value...
To set use
localStorage.setItem('itemName', JSON.stringify(arrayName));
To get use
whatEver = jQuery.parseJSON(localStorage.getItem('itemName'));
Local storage stores strings

How to save an array of JSON objects (with nested objects) to state in React

I'm trying to save an array of JSON objects returned from an API call to state in React (so that I can use the data to render a table). I'm getting the error Error: Objects are not valid as a React child (found: object with keys {street, suite, city, zipcode, geo}). If you meant to render a collection of children, use an array instead.
I can't figure out how to fix this. It looks like the JSON is being stored inside an array as it should be. However, there are also nested objects inside the objects that may be causing an issue, for example:
address": {
"street": "Victor Plains",
"suite": "Suite 879",
"city": "Wisokyburgh",
"zipcode": "90566-7771",
Any assistance would be much appreciated. Here's my code below:
let tableData = []
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(data => {
tableData = data
props.addItem(tableData)
})
Here's the addItem function:
addItem(item) {
this.setState(function(prevState) {
return {
tables: [...prevState.tables, item]
}
})
}
UPDATE
Here's how I am rendering the data:
App.js:
render() {
return (
<div>
{this.state.tables.map(item => {
return (<TableComponent key={item} data={item} />)
})}
</div>
)
}
TableComponent.js:
class TableComponent extends React.Component {
constructor(props){
super(props);
this.getHeader = this.getHeader.bind(this);
this.getRowsData = this.getRowsData.bind(this);
this.getKeys = this.getKeys.bind(this);
}
getKeys = function(){
return Object.keys(this.props.data[0]);
}
getHeader = function(){
let keys = this.getKeys();
return keys.map((key, index)=>{
return <th key={key}>{key.toUpperCase()}</th>
})
}
getRowsData = function(){
let items = this.props.data;
let keys = this.getKeys();
return items.map((row, index)=>{
return <tr key={index}><RenderRow key={index} data={row} keys={keys}/></tr>
})
}
render() {
return (
<div>
<table>
<thead>
<tr>{this.getHeader()}</tr>
</thead>
<tbody>
{this.getRowsData()}
</tbody>
</table>
</div>
);
}
}
const RenderRow = (props) =>{
return props.keys.map((key, index)=>{
return <td key={props.data[key]}>{props.data[key]}</td>
})
}
The error message threw me off here because it made it seem like the issue was in saving the objects to state. However, as was pointed out in the comments, the error happened during rendering. To solve the issue I changed RenderRow to the following:
const RenderRow = (props) =>{
return props.keys.map((key, index)=>{
return <td key={props.data[key]}>{typeof props.data[key] === "object" ? JSON.stringify(props.data[key]) : props.data[key]}</td>
})
}
Specifically, the piece that I changed is to first check whether a specific element is an object, and if it is, to use JSON.stringify() to convert it to a string before rendering it to the screen.