Get data from JSON Response and populate a field and refresh every X Seconds. It doesn't have to be a background task, just while the screen is active.
The responses would be: res.name and res.host. It would also load the current image: res.imgurl
I have tried the code below, but the examples on the react site are quite complex and use lists, I just have simple JSON data, no array.
import { StyleSheet, Text, View } from 'react-native';
export default function App() {
fetch("https://broadcast.truthnetwork.com/play-currentshow.lasso?station=WTRU")
.then(res => res.json())
.then(res => {
console.log("Server response :- \n", res);
})
.catch(error => {
console.log("Error from server :- \n", error);
});
return (
<View style={styles.container}>
<Text style={styles.sometext}>Show Name:</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#375963',
alignItems: 'center',
justifyContent: 'center',
},
sometext: {
color: '#ffffff'
}
});
Here is a sample JSON Response:
{
"name": "Encouraging Word",
"imgurl": "https://broadcast.truthnetwork.com/_img/shows300/4B0FA4EA116331D9A1WH3AE062F0.jpg",
"description": "This is a simple description",
"slug": "encouraging-word-don-wilton",
"weburl": "http://www.theencouragingword.org/",
"feedurl": "http://feeds.feedburner.com/tewfbs",
"host": "Don Wilton",
"showid": "69"
}
I expect app to pull JSON and show the current show, show image and show host and refresh the data every 15-30 seconds (no background task is necessary, just while the screen is active).
Note: This API will work with GET OR POST on station=WTRU
The following approach might come in handy. The following example fetches data from an API every 5 seconds using JavaScript's setInterval. We are making the request once the component first renders, in the componentDidMount method and removing the interval once the component unmounts inside componentWillUnmount.
You need to use state manipulation to update your screen after each api request after every x seconds. Since each state update causes a re-render.
Sandbox Link: https://codesandbox.io/s/falling-pine-81hx4?fontsize=14
Read more about setInterval here: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
state = {
data: {}
}
componentDidMount() {
this.getData();
this.interval = setInterval(() => {
this.getData();
}, 5000);
}
getData = () => {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(json => {
console.log(json);
this.setState({ data: json })
})
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div className="App">
<p>{this.state.data.title}</p>
</div>
);
}
}
Related
Im trying to put a value that I get from axios on a div while I export that function
import React from 'react';
import axios from 'axios';
function callServer() {
axios.get(`http://localhost:${process.env.REACT_APP_SERVER_PORT}`, {
params: {
table: 'querotable',
},
}).then((response) => {
const resp = response.data;
console.log(resp);
return <div>{JSON.stringify(resp)}</div>;
});
}
export function SampleComponent() {
return (
<div>
{callServer()}
</div>
);
}
It shows nothing on div, only on console with the value that I want
Missing return
Your callServer function doesn't return anything. It doesn't even return a Promise. The statement return <div>{JSON.stringify(resp)}</div> is the return for the .then callback -- not for the function itself.
Use Component State
We could return a Promise that resolves to a div but that wouldn't be right. In order to handle asynchronous data in React we want to store that data to state.
const [resp, setResp] = useState();
Side Effects Go in useEffect
We also need to make sure that the axios.get function is only called once instead of on every re-render of SampleComponent. We can do that with a useEffect hook with an empty dependency array.
export function SampleComponent() {
const [resp, setResp] = useState();
useEffect(() => {
axios
.get(`http://localhost:${process.env.REACT_APP_SERVER_PORT}`, {
params: {
table: "querotable"
}
})
.then((response) => setResp(response.data));
}, []);
return (
<div>
<div>{JSON.stringify(resp)}</div>
</div>
);
}
I am trying to fetch JSON data from a php file, which seems to work fine; I can alert values from the JSON. But I want to be able to put these values on the mobile app screen in Text elements or whatever. And I want this to happen when the screen opens, not when a button is pressed. So I made a function that fetches the JSON and I'm trying to return a value in Text elements. This function is called from the rendering. I don't get error messages, but it isn't working. Nothing shows up.
Here is the code:
import React, { Component } from 'react';
import { View, Text, AsyncStorage, Alert } from 'react-native';
import { UsersMap } from '../UsersMap';
import { PrimaryButton } from '../Buttons';
import styles from './styles';
class RestOptions extends Component {
getSearchResults() {
fetch('http://192.168.1.3/Restaurant_App/php/search_results.php')
.then((response) => response.json())
.then((responseJson) => {
var JSON_Test = responseJson["distance"][0];
//Alert.alert(JSON_Test);
return (
<View>
<Text>{JSON_Test}</Text>
</View>
);
}).catch((error) => {
console.error(error);
});
}
setReservation = () => {
this.props.navigation.navigate('SetReservation');
};
render() {
return (
<View>
<UsersMap />
{this.getSearchResults()}
<PrimaryButton
label="Set Reservation"
onPress={() => this.setReservation()}
/>
</View>
);
}
};
export default RestOptions;
This is what happens. The JSON value should appear between the map and the button:
Search Results Screen
First of all, in order to fetch the data as the screen opens, you should use the lifecycle method componentWillMount, which executes before the first render, and then store the result in the component's state. React docs on state and lifecycle
class RestOptions extends Component {
constructor(props) {
super(props);
this.state = {
jsonTest: null
}
}
componentWillMount() {
this.getSearchResults();
}
getSearchResults() {
fetch('http://192.168.1.3/Restaurant_App/php/search_results.php')
.then((response) => response.json())
.then((responseJson) => {
var JSON_Test = responseJson["distance"][0];
//Alert.alert(JSON_Test);
this.setState({ jsonTest: JSON_Test });
}).catch((error) => {
console.error(error);
});
}
//...
Then you can display the value on the render() method:
render() {
return (
<View>
<UsersMap />
<Text>{this.state.jsonTest}</Text>
<PrimaryButton
label="Set Reservation"
onPress={() => this.setReservation()}
/>
</View>
);
}
If the response is an array of values, you can use map() to display each of them in their own Text element:
{this.state.jsonTest.map((value, index) => <Text key={index}>{value}</Text>)}
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.
Am following a tutorial on using Fetch with ReactJS, am able to access a certain section of data from the API/json, but when attempting to access other sections, I get an error. this is the JSON file, which is using the following API: https://api.myjson.com/file1
the code
import React, { Component } from 'react';
import {render} from "react-dom";
import './BuildingSimulation.css';
class BuildingSimulation extends Component {
state = { data: [] }
componentWillMount(){
fetch('https://api.myjson.com/bins/zb5kf', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-type': 'application/json',
'Authorization': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiR3JlZyIsInVuaXF1ZV9uYW1lIjoiZ2dyYWZmIiwibmJmIjoxNTI0ODM5Nzc1LCJleHAiOjE1MjQ5MjYxNzV9.xhvdfaWkLVZ_HLwYQuPet_2vlxNF7AoYgX-XRufiOj0'
},
}
) /*end fetch */
.then(results => results.json())
.then(data => this.setState({ data: data }))
}
render() {
console.log(this.state.data);
return (
<div className="clientContainer">
{
this.state.data.map( item =>(
<div>
<span> {item.clientName} </span>
</div>
))
}
</div>
);
}
}
export default BuildingSimulation;
...so I'm able to get information from the Segments section to display in the page, but how do I also get the ID in the header of the json file and data from the totals section to display in my page as well?
You are storing only segment data from the API response. To access the other data you need to store the entire reponse inside the state. So change
.then(data => this.setState({ data: data.segments }))
to
.then(data => this.setState({ data: data }))
Now you can access all the response data.
Update:
After doing this, the this.state.data represent the json data. So to access segments, use this.state.data.segments. To access totals, use this.state.data.totals and so on.
You can also burst the object like this:
state = {
id = '',
segments: [],
totals: []
}
And on the after fetch do like this:
.then(results => results.json())
.then(({
id = '',
segments = [],
totals = []
}) => this.setState({
id,
segments,
totals
}));
Finally, on your render use the state data like this:
render() {
const {
id,
segments,
totals
} = this.state;
return (
<p>ID: {id}</p>
<ul>
{
segments.map((segment) => <EachSegmentComponent segment={segment} />)
}
</ul>
<ul>
{
totals.map((total) => <EachTotalComponent total={total} />)
}
</ul>
)
}
Hope this helps.
Hi I have this object and will have do some mathematical/statistical operations on it. Now I have to questions:
How do I access it? For Example I'd like to access numbers[0]['B1']
When I do {this.props.numbers[0]['B1']} I get: Cannot read property B1 of undefined.
If I want to do some calculations on those numbers, where would I put them? From my limited experience with react redux, I know I should not do anything like that in reducers, am I right? Would I create more actions (action creators) to eg. get the average B1 number, or any statistical operations on the numbers, etc. Would it be recommended to use 'reselect' for this kind of tasks?
numReducer
import { LIST_NUMBERS, PICK_NUMBER, GET_DATA } from '../actions/actionTypes';
export default (state = [], action = {}) => {
switch (action.type) {
case LIST_NUMBERS:
return action.payload || [];
case PICK_NUMBER:
return action.payload;
case GET_DATA:
return action.payload;
default:
return state;
}
};
actions:
import { LIST_NUMBERS, PICK_NUMBER, GET_DATA } from './actionTypes';
import dataSet from '../data.json';
export const listNumbers = () => {
const nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
return {
type: LIST_NUMBERS,
payload: nums
};
};
export const getData = () => {
return {
type: GET_DATA,
payload: dataSet
};
};
export const pickNumber = (num) => {
return {
type: PICK_NUMBER,
payload: num
};
};
data.json
[
{
"DrawDate": "22-Mar-17",
"B1": 12,
"B2": 6,
"B3": 11,
"B4": 31,
"B5": 27,
"B6": 19,
"BB": 42,
"BS": 1,
"DrawNumber": 2217
},
{
"DrawDate": "18-Mar-17",
"B1": 26,
"B2": 37,
"B3": 8,
"B4": 3,
"B5": 19,
"B6": 41,
"BB": 43,
"BS": 3,
"DrawNumber": 2216
},
....
Home Container
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { listNumbers, pickNumber, getData } from '../actions/numberActions';
import Home from '../components/Home';
const mapStateToProps = state => ({
numbers: state.numbers
});
const mapDispatchToProps = dispatch => (
bindActionCreators({
listNumbers,
pickNumber,
getData
}, dispatch)
);
export default connect(
mapStateToProps,
mapDispatchToProps
)(Home);
Home Component:
import React, { Component } from 'react';
import { View, Text, Button, TextInput } from 'react-native';
export default class Home extends Component {
static navigationOptions = {
title: 'Home Screen',
};
componentDidMount() {
this.props.getData();
}
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Text>####################</Text>
<Text>Intro Screen</Text>
<Text>Number: {this.props.numbers[0]['B1']}</Text>
</View>
);
}
}
EDIT/ADDITION:
As per suggestions below, I've changed the lifecycle method to ComponentWillMount and added a check to see if this.props.numbers is loaded.
import React, { Component } from 'react';
import { View, Text, Button, TextInput } from 'react-native';
export default class Home extends Component {
static navigationOptions = {
title: 'Home Screen',
};
componentWillMount() {
this.props.getData();
}
render() {
if (!this.props.numbers) {
console.log('not yet loaded'); // or a spinner?
}
const { navigate } = this.props.navigation;
return (
<View>
<Text>####################</Text>
<Text>Intro Screen</Text>
<Text>Number: {this.props.numbers[0]['B1']}</Text>
</View>
);
}
}
I still get the same error: Cannot read property 'B1' of undefined. Additionally, the console does not log 'not yet loaded', which would indicate that the numbers object is there - I'm just making an error accessing it.
EDIT2:
import React, { Component } from 'react';
import { View, Text, Button, TextInput } from 'react-native';
export default class Home extends Component {
static navigationOptions = {
title: 'Home Screen',
};
componentWillMount() {
this.props.getData();
}
listNums() {
return this.props.numbers.map((num) => num['B1']);
}
listSingleNum() {
return this.props.numbers[0]['B1'];
}
render() {
if (!this.props.numbers) {
console.log('not yet loaded'); // or a spinner?
} else {
console.log(this.listNums());
}
const { navigate } = this.props.navigation;
return (
<View>
<Text>####################</Text>
<Text>Intro Screen</Text>
<Text>Number: {this.listNums()}</Text>
</View>
);
}
}
So listNums() works fine displaying B1s of each element but if I try to access a single B1 element as in listSingleNum, it throws the error mentioned before: ExceptionsManager.js:63Cannot read property 'B1' of undefined.
How do I access it? For Example I'd like to access numbers[0]['B1'] When I do {this.props.numbers[0]['B1']} I get: Cannot read property B1 of undefined.
It looks like all your react/redux wiring is fine, its just that getData is getting called in componentDidMount so for the first render, the data is not not there yet (see the docs for lifecycle methods order). You can use componentWillMount instead, but I'm still not sure if the data will be available on the first render. To be safe, change the render function to do something different if numbers is undefined (you would have to do this anyway if you ever end up loading the data from a backend somewhere).
NOTE: The following is incorrect - see edit below
render() {
if (!this.props.numbers) {
return null; // or a spinner?
}
const { navigate } = this.props.navigation;
return (
<View>
<Text>####################</Text>
<Text>Intro Screen</Text>
<Text>Number: {this.props.numbers[0]['B1']}</Text>
</View>
);
}
If I want to do some calculations on those numbers, where would I put them? From my limited experience with react redux, I know I should not do anything like that in reducers, am I right? Would I create more actions (action creators) to eg. get the average B1 number, or any statistical operations on the numbers, etc. Would it be recommended to use 'reselect' for this kind of tasks?
This will depend on how intensive the calculations are. If the're pretty cheap, I'd just do them in the render function
import calculateAverage from './somewhere'
...
return (
<View>
<Text>####################</Text>
<Text>Intro Screen</Text>
<Text>Number: {this.props.numbers[0]['B1']}</Text>
<Text>Average: {calculateAverage(this.props.numbers.map((data) => data['B1'])}</Text>
</View>
);
Reselect is a good option if the calculation is expensive so that it doesn't unnecessarily recalculate the values every render. It's also nicer for testing than having the logic in the component itself.
EDIT: Wow... I'm feeling a bit silly at the moment...
this.props.numbers is not undefined (it's defined by the initial state of the reducer). If you check for length it will render (I've replicated all this code and run it myself to be sure this time).
render() {
if (this.props.numbers.length === 0) {
return null; // or a spinner?
}
const { navigate } = this.props.navigation;
return (
<View>
<Text>####################</Text>
<Text>Intro Screen</Text>
<Text>Number: {this.props.numbers[0]['B1']}</Text>
</View>
);
}
It is important to actually return something (or null) within the if statement so that it doesn't hit the undefined value (this.props.numbers[0]).
Explanation (requested in comments)
It all boils down to the component's lifecycle.
When the component mounts it has an empty array, set by the initialState of the reducer
export default (state = [], action = {}) => {
...
};
The mounting lifecycle methods will fire in order. When the componentDidMount (or componentWillMount depending on which update of the question we are at) the state is replaced in the redux store to have the full data set.
After the mounting lifecycle has completed the react-redux will change trigger the props to change, firing off the updating lifecycle methods.
During this stage render is called again, this time with the correct data.
So the component wont "keep re-rendering until the numbers object is not empty", it will re-render whenever the props change, and if the numbers array is not empty, will include desired components.
Returning null is valid in react and is commonly used to prevent components from trying to access props that are not available yet.