Fetch in React making call continuosly - javascript

I am fetching the data from json file but the issue is that when i checked network tab. It's being call continuosly what can i do to resolve the problem.
: Network Tab Image
class Banner extends Component {
constructor(props) {
super(props);
this.state = {
buttonLink:"",
bannerImg:""
};
}
render() {
const a = fetch("./logo.json").then(response => response.json())
.then((temp1)=> {this.setState({buttonLink:temp1.buttonLink,bannerImg:temp1.bannerImg});
});
return(....
<img src={this.state.bannerImg}></img>
....
)

in class component you can use some lifecycle methods like componentDidMount
so put your fetch method inside of a componentDidMount not in render method because whenever you call setState() method, it will call render method again,and inside of your render method you call api and setState again, that makes an inifinite loop, do something like this:
componentDidMount(){
const a = fetch("./logo.json").then(response => response.json())
.then((temp1)=> {this.setState({buttonLink:temp1.buttonLink,bannerImg:temp1.bannerImg});
}

You can't call fetch inside render function.
You can use functional component to easily fetch the data.
import React, { useEffect, useState } from 'react';
const Banner = () => {
const [image, setImage] = useState({ buttonLink: '', bannerImg: '' });
useEffect(() => {
fetch('./logo.json')
.then((response) => response.json())
.then((temp1) => {
setImage({ buttonLink: temp1.buttonLink, bannerImg: temp1.bannerImg });
});
}, []);
return <img src={image.bannerImg}></img>;
};
export default Banner;

Related

How to fetch repeatedly and render updated api data in reactjs?

I am trying to render time data from API endpoint http://worldclockapi.com/api/json/utc/now
The "currentFileTime" property is constantly changing but renders once on load.
I tried setInterval method to update state but it doesn't work. May be I am making some mistake?
This is App.js:
import React , { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = { data: []};
}
async componentDidMount(){
this.fetchData();
}
async fetchData() {
try {
const response = await fetch('http://worldclockapi.com/api/json/utc/now');
if (!response.ok) {throw Error(response.statusText);}
const json = await response.json();
this.setState({ data: json});
console.log(json);
}
catch (error) {console.log(error);}
}
render() {return (<div><h1>worldclockapi.com data (edit App.js)</h1>
<li>currentFileTime: {this.state.data.currentFileTime }</li>
</div> );
}
}
export default App;
How to render and update currentFileTime continuously in react component?
the problem is componentDidMount executed only once, after component mounted for the first time, for example if your state changes componentDidMount is not gonna execute again.
in your case i think it's better to use websockets but if u wanna keep useing this api u can use useEffect hook like below:
const [temp, setTemp] = useState(0)
useEffect(()=>{
setIterval(()=>{
setTemp((prevTemp)=>prevTemp+1)
}, 2000)
}, [])
useEffect(()=>{
fetchData()
}, [temp])
in the above code we have a temp variable and it's value update every 2 second and every time it gets updated the second useEffect run and the data will fetch and as a result the state's gonna change and element gets updated.
Try Calling fecthData recursively upon successful data retrieval like below.
And you don't need to put "async" in front of componentDidMount cause you ain't awaiting anything in the method call.
async fetchData() {
try {
const response = await fetch('http://worldclockapi.com/api/json/utc/now');
if (!response.ok) {throw Error(response.statusText);}
const json = await response.json();
this.setState({ data: json});
console.log(json);
// set the time below to how frequently you wanna update
setTimeout(() => this.fetchData(), 5000);
//
}
catch (error) {console.log(error);}
}
This is using the new hooks. This should solve the problems
import React, {useEffect, useState} from 'react';
import logo from './logo.svg';
import './App.css';
const App = () => {
const [state, setState] = useState({data: []});
useEffect(()=>{
fetchData();
}, [state]);
const fetchData = async () => {
try {
const response = await fetch('http://worldclockapi.com/api/json/utc/now');
if (!response.ok) {throw Error(response.statusText);}
const json = await response.json();
setState({ data: json});
console.log(json);
}
catch (error) {console.log(error);}
}
return (<div><h1>worldclockapi.com data (edit App.js)</h1>
<li>currentFileTime: {state.data.currentFileTime }</li>
</div> );
}
export default App;

How to pass variable when dispatching fetch function in react component?

I have react-redux app which fetching data from my node server, each time i need to fetch something i have to create same action where i change value in fetch, here is questions: How i can pass a variable in order to avoid duplicate code in such situation?
export function fetchProducts() {
return dispatch => {
dispatch(fetchProductsBegin());
return fetch("/api/followers")
.then(handleErrors)
.then(res => res.json().then(console.log(res)))
.then(json => {
dispatch(fetchProductsSuccess(json));
return json;
})
.catch(error => dispatch(fetchProductsError(error)));
};
}
Then i call fetchProduct:
class ProductList extends React.Component {
componentDidMount() {
this.props.dispatch(fetchProducts());
}
I want to have a result that where i call fetchProducts and put a variable, then each time using same action.
You can use following:
// in component:
this.props.dispatch(fetchProducts(id));
// in function
export function fetchProducts(id) {
Moreover, you can use
class ProductList extends React.Component {
componentDidMount() {
this.props. fetch(id);
}
const mapDispatchToProps = (dispatch) => ({
fetch: (id) => dispatch(fetchProducts(id))
});
export default connect(null, mapDispatchToProps)(ProductList)
About your code struct, you should use Axios library to call API, use
promise for wait API call instead of dispatch fetchProductsBegin.
in this case, you can rewrite the code as below:
export function fetchProducts(id) {
return dispatch => {
dispatch(fetchProductsBegin(id));
return fetch(`/api/followers/${id}`)
//...
}
call function
componentDidMount() {
this.props.dispatch(fetchProducts(id));
}

React - Passing fetched data from API as props to components

Iam trying to understand and learn how to pass around data as props to other components to use. Iam trying to build a top-level hierarchy where the API Request is made in a class at top level and then the result is passed around to child components to be used as props and then in states.
The problem is that when i pass the result i get "Object Promise" in my child component. How do I access the data sent as props to child components?
As you can see in my App.js in my render() method that i created a component of the class API and pass the result from the fetchData() method as parameter to the component.
In my API.js class i used console.log to check the result but
the result i get from the logs are:
line 5: {dataObject: Promise}
line 10: undefined
App.js:
import API from './API';
class App extends Component {
componentDidMount(){
this.fetchData();
}
fetchData(){
const url = "https://randomuser.me/api/?results=50&nat=us,dk,fr,gb";
return fetch(url)
.then(response => response.json())
.then(parsedJSON => console.log(parsedJSON.results))
.catch(error => console.log(error));
}
render() {
return (
<div className="App">
<API dataObject={this.fetchData()}/>
</div>
);
}
}
export default App;
API.js
import React from 'react';
class API extends React.Component{
constructor(props){
console.log(props);
super(props);
this.state = {
dataObj:props.dataObject
};
console.log(this.state.dataObject)
}
render() {
return(
<p>""</p>
)
}
}
export default API;
Try changing App.js to this:
import API from './API';
class App extends Component {
componentDidMount(){
this.fetchData();
}
fetchData(){
const url = "https://randomuser.me/api/?results=50&nat=us,dk,fr,gb";
return fetch(url)
.then(response => response.json())
.then(parsedJSON => this.setState({results: parsedJSON.results}))
.catch(error => console.log(error));
}
render() {
return (
<div className="App">
<API dataObject={this.state.results}/>
</div>
);
}
}
export default App;
This makes sure you fetch the data in componentDidMount and it now uses state to store the data which then will be passed into your API component.
If anyone is looking for an answer using Hooks then this might help.
App.js
import API from './API';
function App(props) {
const [result, setResult] = React.useState({});
// similar to componentDidMount
React.useEffect(() => {
this.fetchData();
}, []);
fetchData() {
const url = "https://randomuser.me/api/?results=50&nat=us,dk,fr,gb";
fetch(url)
.then(response => setResult(response.json()))
.catch(error => console.log(error));
}
return (
<div className="App">
<API dataObject={result}/>
</div>
);
}
export default App;
API.js
import React from "react";
function API(props) {
const [result, setResult] = React.useState(props.dataObject);
React.useEffect(() => {
setResult(result);
}, [result]);
return <p>{result}</p>;
}
export default API;
Hope it helps! And let me know if anything is incorrect.
You should fetch data in componentDidMount and not in render. Fetching the data within render causes the API request to be repeated, every time the DOM is re-rendered by react.js.
After making the GET request to the API endpoint, first parse the data into a javascript object, then set the results to state using this.setState from within your component.
From there, you may pass the data held in state to child components as props in the render function.
For example:
const App = (props) =>
<ChildComponent />
class ChildComponent extends React.Component {
constructor(props){
super(props);
this.state = {
results: []
}
}
componentDidMount(){
fetch('/api/endpoint')
.then(res => res.json())
.then(results => this.setState({results})
}
render(){
return <GrandchildComponent {...this.state} />
}
}
const GrandchildComponent = (props) =>
<div>{props.results}</div>

"Can only update a mounted or mounting component " warning in componentDidMount

Theoretically, I should be able to asynchronously fetch some data and update my component inside of componentDidMount. Here's my component:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
constructor () {
super()
this.state = {}
}
componentDidMount () {
fetch('/api/sessions')
.then(response => response.json())
.then(data => {
this.setState({ sessions: data.body })
})
}
render () {
return (
<div>
<h1>Sessions</h1>
<ul>
{this.state.sessions && this.state.sessions.map(session => {
return <li key={`session-${session._id}`}>{session._id}</li>
})}
</ul>
</div>
)
}
}
ReactDOM.render(<App />, document.querySelector('#root'))
The component renders, and re-renders when the data is received. But I'm getting a warning:
Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.
Please check the code for the App component.
Shouldn't I be able to assume that componentDidMount implies the component actually mounted? What am I doing wrong here?
To prevent it You can maintain a state say isMounted and update it on componentWillMount and componentWillUnmount. And whenever you are asynchronously trying to set state, first check whether the component is still mounted or not.
componentWillMount = () => {
this.setState({
isMounted: true
})
}
componentWillUnmount = () => {
this.setState({
isMounted: false
})
}
componentDidMount () {
fetch('/api/sessions')
.then(response => response.json())
.then(data => {
if (this.state.isMounted) {
this.setState({ sessions: data.body })
}
})
}

redux - same data fetch HOC component called multiple times

I have a React decorator component which is connected to the Redux store and I'm using it to dispatch an action (which is used to get some data from an API endpoint) and show a Loader Component. Then, once the data is fetched, it shows a wrapped component.
It looks like this:
const loadData = LoaderComponent => WrappedComponent => {
class loadDataHOC extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
const {fetchData, isLoading} = this.props;
if(!isLoading){
fetchData();
}
}
render() {
const {data, isLoading} = this.props;
if (isLoading) {
return <LoaderComponent />;
}
return <WrappedComponent data={data} />;
}
}
const mapStateToProps = state => {
return {
data: getData(state),
isLoading: getIsLoading(state)
};
};
const mapDispatchToProps = dispatch => bindActionCreators({getData}, dispatch);
return connect(mapStateToProps, mapDispatchToProps)(loadDataHOC);
};
export default loadData;
This component is meant to be reusable so I can use it to fetch and store the same data from different presentational components. What I'd like to do now is to use this component in two different parts of the same view, like this:
const EnhancedComponent1 = loadData(Spinner)(MyPresentationalComponent1);
const EnhancedComponent2 = loadData(Spinner)(MyPresentationalComponent2)
The problem is that the two EnhancedComponent both fire fetchData() because they are mounted together and therefore the isLoading prop is false in both the function calls.
For now I've solved it by checking the isLoading prop inside the action so the second call is immediately stopped, but I'm not sure if this is the best way to deal with it.
const getData = () => (dispatch, getState) => {
if(getIsLoading(getState())) {
return;
}
dispatch(getData());
...
};
Another way to do it would be to create only one parent enhanced component just to fetch the data and then two presentational components down the tree that only access the state, but I'd like to fetch the data as close as possible to the presentational component.
Thanks

Categories

Resources