Short story: I am making my own GIS (geographic information system) and want to be able to upload JSON files with geographical data. I do not however want to save files in a database, just in a list. Furthermore I'm using Context to parse data to the <MAP/> (leaflet) component.
My problem is that when pushing the JSON to the list it is not recogniezed as a JSON but as string. How can I solve this problem.
I am quite new to react so I am open for suggestions to solve it differently.
Here is my code
import React, {createContext, useState} from 'react';
import "../../App.css";
import data from '../../Layers/layer1.json'
import data1 from '../../Layers/layer2.json'
export const FileContext = createContext()
const layerList = []
function updateList(layer){
layerList.push(layer)
}
export const FileProvider = (props) => {
const [layer, setLayer] = useState(
layerList
)
return(
<FileContext.Provider value = {[layer, setLayer]}>
{props.children}
</FileContext.Provider>
);
}
// Create an object of formData
const onFileChange = e => {
const fileReader = new FileReader();
fileReader.readAsText(e.target.files[0], "UTF-8");
fileReader.onload = e => {
console.log(e.target.result);
updateList(e.target.result);
console.log(layerList)
};
//console.log(layerList)
}
function FileUpload() {
return (
<div>
<div id='fileupload'>
<input type="file" onChange={onFileChange} />
</div>
</div>
);
}
export default FileUpload;
And this is the Map component
import { Map, TileLayer, GeoJSON} from 'react-leaflet'
import "../App.css";
import data from '../Layers/layer1.json'
import { FileContext } from '../LandingPage/ToolbarComponents/FileUpload';
function MapOslo() {
const [layer, setLayer] = useContext(FileContext)
return (
<Map center={[59.93, 10.75]} zoom={4}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='© OpenStreetMap contributors'
/>
<GeoJSON data={layer} style={['color','#006400']} />
</Map>
);
}
export default MapOslo;
To convert a String to JSON you can use JSON.parse(str)
var jsonStr = "{test: 'hi'}";
var json = JSON.parse(jsonStr);
So I think you have to implement parse here: updateList(JSON.parse(e.target.result));
Related
I have data in json format and I want to loop through it to render the same component (ContentThumbnail) eight times but with different titles and other content.
I have tried creating a function which accepts four parameters to achieve this. Here is the function I've written in a separate file called RenderContent.js:
import React from 'react';
import ContentThumbnail from './ContentThumbnail';
function RenderContentThumbnail(data, sectionName, wrapperStart, wrapperEnd) {
return (
<div>
{data
.filter(d => d.sectionName === { sectionName })
.map(filteredSection => (
{wrapperStart}
<ContentThumbnail {filteredSection.title} />
{wrapperEnd}
))}
</div>
);
}
export default RenderContentThumbnail;
And here is where I'm trying to execute that function in my component DefaultDashboard.js:
import React, { useEffect } from 'react';
import RenderContent from '../../content-thumbnail/RenderContent';
const DefaultDashboard = () => {
const { data } = useFetchData({ queryString: `${contentLibraryApiUrl}/GetContentForPage/Home` });
return (
RenderContentThumbnail(data, "topSection", "<div>", "</div>")
);
};
export default DefaultDashboard;
Is anyone able to help me see where I'm going wrong? I'm getting errors inside my map function and the page won't render at all.:(
Many thanks!
Katie
UPDATE!
I have made a tweak to the code to specify the prop, which is called "title", but I'm getting the following:
You should change the way you are rendering RenderContent:
const DefaultDashboard = () => {
const { data } = useFetchData({ queryString: `${contentLibraryApiUrl}/GetContentForPage/Home` });
return (
<RenderContent data={data} sectionName="topSection" wrapperStart="<div>" wrapperEnd= "</div>")
);
};
You can make it a lot easier, removing RenderContentThumbnail:
const DefaultDashboard = () => {
const { data } = useFetchData({ queryString: `${contentLibraryApiUrl}/GetContentForPage/Home` });
return (
{data
.filter(d => d.sectionName === "topSection")
.map(filteredSection => (<div>
<ContentThumbnail title={filteredSection.title} />
</div>))
}
);
};
export default DefaultDashboard;
Short story: I am making my own GIS (geographic information system) and want to be able to upload JSON files with geographical data. I do not however want to save files in a database, just in a list. Furthermore I'm using Context to parse data to the <MAP/> (leaflet) component.
When I upload new JSONs to the layerList it gets updated but the <MAP/> component does not re-render.
I might be using a weird way of updating the state, but I dont know how to do it differently.
Here is my code
import React, {createContext, useState} from 'react';
import "../../App.css";
import data from '../../Layers/layer1.json'
import data1 from '../../Layers/layer2.json'
export const FileContext = createContext()
const layerList = [data]
function updateList(layer){
layerList.push(layer)
}
// Create an object of formData
const onFileChange = e => {
const fileReader = new FileReader();
fileReader.readAsText(e.target.files[0], "UTF-8");
fileReader.onload = e => {
updateList(JSON.parse(e.target.result));
console.log(layerList)
};
//console.log(layerList)
}
export const FileProvider = (props) => {
const [layer, setLayer] = useState(
layerList
)
return(
<FileContext.Provider value = {layer}>
{props.children}
</FileContext.Provider>
);
}
function FileUpload() {
return (
<div>
<div id='fileupload'>
<input type="file" onChange={onFileChange} />
</div>
</div>
);
}
export default FileUpload;
Your layerList exists and is maintained outside of the scope of your component. It's not aware of changes being made to the layerList in this fashion. You need to move your logic a little to look more like this:
import React, {createContext, useState} from 'react';
import "../../App.css";
import data from '../../Layers/layer1.json'
import data1 from '../../Layers/layer2.json'
export const FileContext = createContext()
// Create an object of formData
const onFileChange = e => {
const fileReader = new FileReader();
fileReader.readAsText(e.target.files[0], "UTF-8");
fileReader.onload = e => {
updateList(JSON.parse(e.target.result));
console.log(layerList)
};
//console.log(layerList)
}
export const FileProvider = (props) => {
const [layerList, setLayerList] = useState([data]);
const updateList (layer) => {
setLayerList([layer, ...layerList]); //This creates a new array in memory, so it will trigger a rerender.
};
useEffect(() => {
//Update json file logic
}, [layerList])
return(
<FileContext.Provider value = {layerList, setLayerList}>
{props.children}
</FileContext.Provider>
);
}
function FileUpload() {
//change this component to be a context consumer and the 'onChange' event can fire the `setLayerList` function to update everything.
return (
<div>
<div id='fileupload'>
<input type="file" onChange={onFileChange} />
</div>
</div>
);
}
export default FileUpload;
I am working on kepler.gl .... wright now i am reading data from an api and plot that data in keplr.gl and that's working fine here is the code ...
import keplerGlReducer from "kepler.gl/reducers";
import { createStore, combineReducers, applyMiddleware } from "redux";
import { taskMiddleware } from "react-palm/tasks";
import { Provider, useDispatch } from "react-redux";
import KeplerGl from "kepler.gl";
import { addDataToMap } from "kepler.gl/actions";
import useSwr from "swr";
const reducers = combineReducers({
keplerGl: keplerGlReducer
});
const store = createStore(reducers, {}, applyMiddleware(taskMiddleware));
export default function App() {
return (
<Provider store={store}>
<Map />
<csv/>
</Provider>
);
}
function Map() {
const dispatch = useDispatch();
const { data } = useSwr("covid", async () => {
const response = await fetch(
"https://gist.githubusercontent.com/leighhalliday/a994915d8050e90d413515e97babd3b3/raw/a3eaaadcc784168e3845a98931780bd60afb362f/covid19.json"
);
const data = await response.json();
return data;
});
React.useEffect(() => {
if (data) {
dispatch(
addDataToMap({
datasets: {
info: {
label: "COVID-19",
id: "covid19"
},
data
},
option: {
centerMap: true,
readOnly: false
},
config: {}
})
);
}
}, [dispatch, data]);
return (
<KeplerGl
id="covid"
mapboxApiAccessToken="pk.eyJ1IjoiYWxpcmF6YTcwNSIsImEiOiJjazh5d2hjb3AwOHBqM2VsY21wOHo5eXprIn0.9G5CE4KqfbvU9HQ6WBuo3w"
width={window.innerWidth}
height={window.innerHeight}
/>
);
}
now i want to read data from jason or csv file and plot that data into kepler.gl map .. how can i do this can anyone help me ?..... thanks
In React do it like this
import datajson from './yourdata.json';
const data=datajson;
easiest way would be to place your json or csv file beside your code and
import your json file:
const dataJson = require('./data.json');
change your data source to your json:
const data = dataJson
// or dataJson.data depending on your json structure
or if your data is in csv format the fastest way that comes to mind is installing the csv-parse and then:
const data = parse(csvData, {
//config based on your csv format
columns: true,
skip_empty_lines: true
});
hope this is what you are looking for
I'm fairly new to React.
I am trying to import this following test json data file into my React graph.
{
"data": [
{"datetime": "", "data": [1,2,3,4]},
{"datetime": "", "data": [1,2,3,4]}
]
}
The x-axis is the time so it should not matter very much.
But I don't know how to read the data into the y-axis.
This is my Graph.js:
import React, { Component } from 'react';
import Highcharts from 'highcharts';
import socketIOClient from "socket.io-client";
import {
HighchartsChart, Chart, withHighcharts, XAxis, YAxis, Title, Legend, LineSeries
} from 'react-jsx-highcharts';
import './Graph.css'
var jsonData = import '../jsonData.json' //pass the json file location
class Graph extends Component {
constructor (props) {
super(props);
this.addDataPoint = this.addDataPoint.bind(this);
const now = Date.now();
this.state = {
data: []
}
}
componentDidMount() {
//THIS is to add points from json
//Please don't mind the stuff here--------
//This is for another graph
var sensorSerial = this.props.match.params.sensorId
const socket = socketIOClient("http://localhost:5001/test");
socket.on("newnumber", data => this.addDataPoint(data));
//----------------------------------------
}
addDataPoint(data){
//This is for another graph------------
if(data.sensorSerial == this.props.match.params.sensorId){
var newData = this.state.data.slice(0)
console.log(new Date(data.dateTime.split(' ').join('T')))
newData.push([new Date(data.dateTime.split(' ').join('T')), data.number])
this.setState({
data: newData
})
}
//-------------------------------------
}
render() {
// const {data} = this.state;
const plotOptions = {
series: {
pointStart: new Date("2019-04-04T10:55:08.841287Z")
}
}
return (
<div className="graph">
<HighchartsChart plotOptions={plotOptions}>
<Chart />
//Title
<Title>Sensor Data</Title>
//Legend
<Legend layout="vertical" align="right" verticalAlign="middle" >
<Legend.Title>Legend</Legend.Title>
</Legend>
<XAxis type="datetime">
<XAxis.Title>Time</XAxis.Title>
</XAxis>
<YAxis>
<YAxis.Title>Y-axis</YAxis.Title>
<LineSeries name="Channel 1" data={this.state.data}/>
</YAxis>
</HighchartsChart>
</div>
);
}
}
export default withHighcharts(Graph, Highcharts);
If this is a duplicate question, please let met know!
I'm not a frequent on Stakoverflow, so if there's anything I can do to improve my post, please let me know.
Thank you for your time!
I have a json file named autofill.json and it's created to autofill a search bar when pressed on.
the autofill.json is a test file that's why it looks like this.
[
{
"a": {
"apple": {
"name": "apple",
"href": "https://www.apple.com/"
},
"armadillo": {
"name": "armadillo",
"href": "https://www.armadillo.com/"
}
},
"b": {
"box": {
"name": "apple",
"href": "https://www.berserk.com/"
},
"berserk": {
"name": "berserk",
"href": "https://www.berserk.com/"
}
}
}
]
The .json file is then fetched in the file named FetchAndParseResults.js
import fetch from 'isomorphic-fetch'
const FetchAndParseResults = (url) => {
return fetch(url).then(response => {
const parsedJson = response.json()
return parsedJson
})
}
export default FetchAndParseResults
The data that gets fetched is used in searchcontainer.js where everything gets placed in, the search etc.
import React from 'react'
import Searchbar from './index.js'
import FetchAndParseResults from './FetchAndParseResults.js'
class SearchContainer extends React.Component {
state = {
results: []
}
performSearch = event => {
return FetchAndParseResults('static/autofill.json').then(data => {
this.setState({ results: data })
})
}
render () {
console.log('performSearch event', this.performSearch)
console.log('data inside performSearch', this.state.results)
return (
<Searchbar
performSearch={this.performSearch}
results={this.state.results}
/>
)
}
}
export default SearchContainer
Then to map through the data that is in autofill.json there is a file named autofill.js
import React from 'react'
import PropTypes from 'prop-types'
import Styles from './searchbar.scss'
const AutoFill = (props) => {
console.log('proppppppsss', props)
const results = props.results || []
return (
<ul className={Styles.searchUl}>
{results.map(({ name, href }) => (
<li className={Styles.searchLi} key={href}>
<a className={Styles.searchA} href={href} target='_blank' rel='noopener noreferrer' key={href}>
{name}
</a>
</li>
))}
</ul>
)
}
AutoFill.propTypes = {
results: PropTypes.array
}
export default AutoFill
the Searchbar component in (index.js) that is being used in searchcontainer.js
import React from 'react'
import Styles from './searchbar.scss'
import Icon from '../../components/icon/icon'
import Search from '../../components/form-input/search'
import AutoFill from './autofill'
import PropTypes from 'prop-types'
export default class Searchbar extends React.Component {
constructor (props) {
super(props)
this.state = {
className: Styles.input,
icon: Styles.icon__wrapper,
value: []
}
this.input = React.createRef()
}
openInput = () => {
this.setState({
className: Styles.input__active,
icon: Styles.iconWidth
}, () => {
this.input.focus()
})
this.props.onOpen && this.props.onOpen()
}
closeInput = () => {
this.setState({
className: Styles.input,
icon: Styles.icon__wrapper
})
this.props.onClose && this.props.onClose()
}
handleChange = event => {
let value = event.target.value
this.setState({ value })
this.props.performSearch(value)
}
handleSubmit = event => {
event.preventDefault()
}
render () {
console.log('results', this.props.results)
console.log('state.value', this.state.value)
return (
<div>
<form onSubmit={this.handleSubmit} className={Styles.search}>
<div className={this.state.icon}>
<Icon className={Styles.icon__wrapper} iconName='faSearch' onClick={this.openInput} />
</div>
<Search autoComplete='off' value={this.state.value} onChange={this.handleChange} id='search' tabIndex='0' myref={input => { this.input = input }} className={this.state.className} onBlur={this.closeInput} placeholder='Search' />
</form>
<div>
<AutoFill results={this.props.results} />
</div>
</div>
)
}
}
Search.propTypes = {
performSearch: PropTypes.func,
results: PropTypes.array
}
When i try to refer to a what is in the json file from the search i receive the error,
GET http://localhost:3000/[object%20Object] 404 (Not Found)
And
about:1 Uncaught (in promise) SyntaxError: Unexpected token < in JSON
at position 0
The second error is fixed by doing
const parsedJson = response.text(
instead of
const parsedJson = response.json()
to get more information where/what the error takes place. But by doing this i receive the error,
searchcontainer.js:12 Uncaught (in promise) TypeError: Cannot read property 'results' of undefined
I've tried to run it from npm build instead of running it in a dev environment which didn't fix it.
I read that a mock url should work but then again i want to acces it from a file and not from a url?
Any help would be highly appreciated and looked into.
The problem is most likely in the fetch call. If you look at the error message GET http://localhost:3000/[object%20Object] 404 (Not Found)
You can see that it is trying to append an object to the URL localhost:3000/.
You are getting the Unexpected token < in JSON at position 0 error because the response of your fetch request is probably a 404 page. The < is most likely the first char of <html>
To access the JSON object in your React files, you can simply do an importation like so;
import * as autofillData from 'autofill.json';
It will be returned as a JSON object.
I believe you are using the isomorphic-fetch package wrongly, if you look at their source code, https://github.com/matthew-andrews/isomorphic-fetch/blob/master/fetch-npm-node.js#L5 , they are accepting a URL to make a call to the API URL which will return a promise or a JSON object depending on the implementation of the API that you are calling.
If you were to dive deeper into the open-source code here (https://github.com/matthew-andrews/isomorphic-fetch/blob/master/fetch-npm-node.js#L8) , you will notice that isomorphic-fetch package is using another package node-fetch to do their fetch call, which accepts the API URL and the method request options to call the API with. (As stated here; https://github.com/bitinn/node-fetch/blob/master/src/index.js#L34)
To continue with your test, perhaps this might be the solution you'd prefer?
import fetch from 'isomorphic-fetch';
import * as autofillData from 'autofill.json'; //test data
const FetchResults = event => {
return fetch('/https://jsonplaceholder.typicode.com/todos/1'') //mockURL, to be replaced with real API
.then(response => {
// const parsedJson = response.json(); // TODO: un-comment this line when the real API url is usable
const parsedJson = autofillData; // TODO: remove this line when mocking is done and the real API URL is ready
return parsedJson;
})
}
export default FetchResults;
To have a mock URL placeholder, I would suggest https://jsonplaceholder.typicode.com/ to prevent your fetch result to return an unexpected error during test mocking.
Hope this is helpful.
The question has been solved, The main issue was with defining const names such as const results = [] which should've been const results = props.results || [].
The code has been updated incase you have problems aswell.