React and Reloading API Call - javascript

I'm working on a project in React that pulls random data from an API and processes it. Currently, on loading the page, that component that actually pulls the API doesn't load by design.
So to keep things simple, I have two Components. I load Component1 and using a state showDiv: false I don't load Component2 with the API call. I have a button that when clicked changes the state to true, and by doing that, loads Component2 inside Component1.
Now what I want to do is have Component2 hidden again, and then brought back, and call a new set of data from the API and process it. I wrongly assumed that once Component2 was pulled off the page, that bringing it back would load it from scratch eg. rerun the componentWillMount() function inside Component2, which is where my API call is located. Now that I see that's not the case, I'm not sure how to accomplish that without reloading the page entirely.
EDIT:
Here's the component to be rendered:
(This has been heavily simplified. The API pulls an array, and that array is processed into an array of objects, and then it is placed in the state. The component's render is then populated by content taken from that processed array from the state)
class Questions extends Component {
constructor(props) {
super(props);
this.state = {
isQuestionLoaded: false,
questions: [],
};
}
componentWillMount() {
fetch(this.props.category)
.then(results => {
return results.json();
}).then(data => {
let questions = data.results.map((question) => {
return question;
})
this.setState({questions: questions, isQuestionLoaded: true});
})
}
Here's the App.js render of this component:
render() {
let questionRender = this.state.showQuestions ?
<Questions
category={ this.state.categoryActive }
questionNumber={ this.state.questionNumber }
onClickCorrect={ this.nextQuestionCorrect }
onClickCorrectLast={ this.lastQuestionCorrect }
onClickIncorrect={ this.nextQuestionIncorrect }
onClickIncorrectLast={ this.lastQuestionIncorrect }
score={ this.state.score }
correct={ this.state.correct }
answerCorrect={ this.state.answerCorrect } />
: null;
Here's the function that takes the component away:
lastQuestionCorrect() {
this.setState({
showQuestions: false,
showFinal: true
});
}
Here's the function that brings the component back:
onClickReplay() {
this.setState({
showQuestions: true,
showFinal: false,
});
}

A new array was being loaded, the problem was one of my states in App.js was not being reset correctly by onClickReplay()

Related

How to load new axios data in react component

I've got a navigation menu that correctly loads components with react-router using <Link to={"/entity"}>Entities</Link>
The components load data using axios and display it in tables. What I'm struggling to accomplish is loading new data when clicking the <link> a subsequent time.
class List extends Component {
constructor() {
super();
this.state = { entities: [], loading: true};
}
componentDidMount() {
this.getData();
console.log('componentDidMount');
console.log(this.state.entities);
}
getData() {
axios.get(this.props.url).then(response => {
this.setState({ entities: response.data, loading: false})
})
}
render() {
...
}
This works well for loading a single set of data. But if I edit a row and open the list again it will have the originally retrieved. Which is the correct behaviour given that code, but how can I reload that? I've tried componentDidUpdate but that creates an infinite loop. I'm assuming due to componentDidUpdate changing the DOM which then again calls componentDidUpdate again.
componentDidUpdate(prevProps) {
this.getData();
console.log('componentDidUpdate');
}
I thought about doing something like adding onClick={this.handleClick} to the menus and changing states or passing values to indicate a new click. But there must be a way to catch an update from router and not just a change of the component.
If you use setState inside componentDidUpdate it updates the component, resulting in a call to componentDidUpdate which subsequently calls setState again resulting in the infinite loop. You should conditionally call setState and ensure that the condition violating the call occurs eventually e.g:
componentDidUpdate(previousProps, previousState) {
if (previousProps.data !== this.props.data) {
this.setState({/*....*/})
}
}
The solution I came up with was to add a datestamp to the link and compare that to the previous timestamp.
<Link to={{"/entity", state: { update: + new Date() } }}>Entities</Link>
componentDidUpdate(prevProps, prevState) {
if (prevProps.update !== this.props.update) {
this.setState({ loading: true })
this.getData();
//console.log('Data Updating - ' + this.props.update);
}
}

how to pass the state property as prop to show and hide component in react when it's asynchronous update

i have a navbar when i click on it i'm setting my state to true and passing it in my component as prop but the function takes time update it and my props are sent as false how can i send my updated prop when the state update is complete
navbar.js
constructor(props) {
super(props);
this.state = {
burgerClicked:false,
};
}
burgerClicked=()=>{
this.setState({ burgerClicked: true },()=>{ this.props.clicked(this.state.burgerClicked)})
}
app.js
constructor(props){
super(props);
this.state={
open:false,
}
}
openSideNav =(burgerClicked)=>{// the time is taking here to update it send open as false
this.setState({open:burgerClicked},()=>{
console.log(this.state.open)
});
<Navbar clicked={this.openSideNav} tokenRequested={this.request}/>
<Header open={this.state.open} />
header.js
constructor(props) {
super(props);
this.state = { showSideNav: false };
}
UNSAFE_componentWillReceiveProps(){
//
console.log(this.props.open,this.state.showSideNav);//this.props.open is false
if (this.props.open) {
this.setState({showSideNav:this.props.open},()=>{
console.log(this.state.showSideNav); //this.state.showSideNav dont print on first click but print true on second click on burger
})
}
console.log(this.props.open,this.state.showSideNav); //this.props.open is false
}
{(() => {
if (!this.state.showSideNav) {
return null;
}
return(
<SideNavbar closeNav={this.closeSideNav}/>
)
})()}
This answer may be more than what you are asking for, but here it goes:
In this example you have three states: burgerClicked (in Navbar.js), open (in App.js), and showSideNav (in header.js).
It looks like all three of those keep track of the same thing: "is the side nav open?". You don't want that, you should have only one component that keeps track of that.
Based on your example, I would say that only App.js should have that state, and pass its value to Navbar and header as props.
In Navbar.js, the onClick just needs to call the clicked function, without passing any param. Then, App.js figures out whether to set open to true or false, (which most likely just means toggling it).
Once App.js updates its state, it will pass that updated value down to header and Navbar and that's the beauty of React.
Your Question is kinda not understandable and only can figure out you want to use conditional rendering.
Read this https://reactjs.org/docs/conditional-rendering.html
Hope it will help you :)

How do I return a JSON array in React from a local file and have it map through all of my items?

Notes: using React for this.
Basically, I'm just trying to make a list of anchor elements from a list of links I have stored locally in a json file. I can confirm that the file is successfully seeing the "endpoints" data through console logs. However, the page just renders a white page and it doesn't look like the state is getting set correctly with the imported array.
So, this is what my file looks like right now (Any help would be greatly appreciated!):
import React from 'react';
import endpoints from './endpoints.json';
class Link extends React.Component{
constructor(props){
super(props);
this.state = {
error: null,
isLoaded: false,
myData: []
};
}
componentDidMount() {
let myData = endpoints.map((data, key) => {
console.log(endpoints);
console.log(endpoints[0].key);
return(
<a className="aLink" href={endpoints.link} key={endpoints.key} >{endpoints.name}</a>
)
})
this.setState({myData: myData});
console.log(this.state.myData);
}
render() {
const { error, isLoaded } = this.state;
if (error) {
return <div className="errorM">Error: {error.message}</div>;
} else {
return(
<div>
{this.state.myData}
</div>
)
}
}
}
export default Link;
You seem to be trying to render from the initial response (endpoints) rather than the map value (data). Change
href={endpoints.link} key={endpoints.key} >{endpoints.name}
to
href={data.link} key={data.key} >{data.name}
Well, this was one of those classic, ask a question and then immediately figure out the answer. Basically, where I'm mapping each item, I set an argument called "data". Instead of calling "endpoints.xxx" it should be "data.xxx" for everything. Then, everything renders fine. :)

Managing React component behavior during update

I'm new to ReactJs, and working with the ExtReact framework. I'm displaying a grid, and made a pagination, which is working fine.
I customed the spinner displayed when datas are loading, and it works fine when the "component did mount".
But, when I update the component (like when I switch the page using the ExtReact paginator plugin), there is another native spinner displayed, and I want to custom it too.
My problem is that I don't find a proper way to do it with lifeCycle components methods, since the componentWillUpdate method is deprecated.
I first had only one 'isLoading' state, but I added 'isUpdating' since 'isLoading' was modified after the first render because of the way the store data are loaded.
The isUpdating state seems to stay false, this is what is displayed by the console:
Snapshot
Is updating:false
Updated!
Is updating:false
Saul Goodman!
​
Here is my Component code:
import React, {Component} from 'react';
import {Grid, Column, Toolbar, SearchField} from '#sencha/ext-modern';
import {withRouter} from "react-router-dom";
import Spinner from '../../resources/components/Spinner';
Ext.require('Ext.grid.plugin.PagingToolbar');
class Subscriptions extends Component {
state = {
isLoading: true,
};
store = Ext.create('Ext.data.Store', {
fields: ['Field1', 'Field2', 'Field3'],
autoLoad: false,
pageSize: 30,
proxy: {
type: 'rest', // refers to the alias "proxy.ajax" on Ext.data.proxy.Ajax
url: 'niceadress',
reader: {
type: 'json'
}
}
});
/**
* Loading the datas into the store and then removes the spinner when fetched successfully
*/
componentDidMount() {
this.store.load({
callback: function (records, operation, success) {
this.setState({
isLoading: (!success),
});
console.log("Saul Goodman!");
},
scope: this
});
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
if (nextState.isLoading === true){
return false;
} else {
return true;
}
};
getSnapshotBeforeUpdate(prevProps, prevState) {
this.setState({
isLoading: true,
});
console.log('Snapshot');
console.log('Is loading:' + this.state.isLoading)
return prevState;
}
componentDidUpdate(prevProps, prevState, snapshot) {
this.setState({
isLoading: (!prevState.isUpdating),
});
console.log('Updated!');
console.log('Is loading:' + prevState.isLoading)
}
render() {
if (this.state.isLoading) return <Spinner/>;
return (
<Grid
store={this.store}
plugins={['pagingtoolbar', 'listpaging']}
>
The Grid
</Grid>
)
}
}
export default withRouter(Subscriptions);
Any idea of what I'm doing wrong?
EDIT: Well, I first thought that the store load wasn't going to trigger the componentDidUpdate method, that's why I wrote the isUploading state separately. I'm removing it but I still didn't solved my problem.
How can I do to prevent the virtualDOM to re-render after the setState call in componentDidUpdate?
I'm looking for an elegant way to break this loop.
The isUpdating state seems to stay false, this is what is displayed by
the console
This is because your shouldComponentUpdate is returning false when isUpdating is true.
There is also a typo in your componentDidUpdate's setState
Page changes using ExtReact Paginator strangely aren't triggering the ComponentDidUpdate method. Then, all this doesn't look to be the proper way to change the component behavior. ExtReact documentation looks a bit confusing about this point, I can't see how to override the pagingtoolbar component easily.
Thanks for your answers!

Wait for data to be fetched in child components, then render

I have a React app that uses multiple fetch calls throughout different components. In Home page component, I have imported smaller components, all of whom have it's own fetch call.
render() {
return (
<React.Fragment>
<Banner/>
<Services />
<About />
</React.Fragment>
)
}
Banner, Services and About have their own fetch calls to different endpoints, now my question is because the response is a little bit on the slower side, how to wait for all of the child components to fetch data, then render the Homepage component. I have tried to put the state of isLoading and add a loader to wait for components to fetch, but I don't know what to wait for to set isLoading to false.
...how to wait for all of the child components to fetch data, then render the Homepage component
You don't. Instead, you move the fetches to the Homepage component's parent, and then have that parent only render the Homepage component when it has all of the necessary information to do so. In React parlance, this is "lifting state up" (e.g., up the hierarchy to the parent).
While you could render the Homepage in a "loading" form, and have it render its child components in a "loading" form, and have the child components call back to the Home page to say they have their information now, that's more complicated than simply lifting the state up to the highest component that actually needs it (so it knows it can render the Homepage).
As #TJCrowder mentioned in his answer, You'll need to lift your state up and keep it in the parent component. Make a network request there and pass the data to your child component as props. You can read more about lifting-state-up here
class YourComponent extends React.Component {
state = {isLoading: true, isError: false, banner: null, services: null, about: null};
async componentDidMount() {
try {
const [banner, services, about] = await Promise.all([
// all calls
]);
this.setState({ isLoading: false, banner, services, about });
} catch (error) {
this.setState({ isError: true, isLoading: false });
}
}
render() {
if (this.state.isLoading) {
return <div>Loading...</div>
}
return (
<React.Fragment>
<Banner data={this.state.banner} />
<Services data={this.state.services} />
<About data={this.state.about} />
</React.Fragment>
)
}
}
using promises in fetch you could, as suggested, have a isLoaded property state determine whether or not a component should render or not.
class ShouldRender extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
isLoaded: false,
}
}
componentDidMount() {
fetch('http://someresource.com/api/resource')
.then(res => res.json())
.then(data => {
this.state({
data,
isLoaded: true,
});
})
}
render() {
const { isLoaded } = this.state;
if (isLoaded) {
return <MyAwesomeReactComponent />
}
return null;
}
}
So once the state is updated it will trigger a rerendering of the component with the new state that will render the if statement true and you're JSX will appear.

Categories

Resources