React - Render page after props receive new data - javascript

I have few divs in my page and I want to display the latest data in them getting from an API after every 15 sec without showing the refresh of the page.
The issue is this.props have latest data but its not rendering on the page. Do I need to use componentWillUpdate ? or componentWillReceiveProps ? as both of them are obsolete.
How can I make sure to get the latest data on the page after every 15 sec.
componentDidMount(){
this.updateTimer = setInterval(() => this.getData(), 15000);
}
getData = e => {
const _this = this
admin.getDataFromAPI()
.then((response) => {
if(_this.props.getData.error){
this.context.store.dispatch(receivedDataFromAPI(response));
}
else if(_this.props.getData.data){
let updatedData = response.data;
let oldData = _this.props.getData.data;
oldData.map((col, i) => {
col.state = updatedData[i].state;
col.name = updatedData[i].name;
col.xValue = updatedData[i].xValue;
})
}
})
.catch((error) =>{
this.context.store.dispatch(failedDataFromAPI(error));
});
};
componentWillUnmount(){
clearInterval(this.updateTimer);
}

There are at least two ways of doing this
Using props you can tell parent to send different props which will cause rerender. I personally do not recommend this for sake of complexity.
Use states. Using states is really easy and I will show it to you in this example:
import React, { Component } from 'react';
class Results extends Component{
constructor(props){
super(props);
this.state = {
results : [] //Your empty array or initial value that you set
}
}
componentDidMount(){
//Here you can set interval which will make requests to API and wait for answers
//once you get response you do :
this.setState({
results : responseFromAjax
});
}
componentWillUnmount(){
}
componentDidUpdate(prevProps,prevState){
}
render(){
return(
<p>{this.state.results}</p>
)
}
}
export default Results;
In constructor of the Class Component you set its state which is basically JSON.
Once you want to change the value that is being displayed, you just call this.setState function and it pass the updated key:value pair which will replace the ones that are currently active.

Related

React: Can't access array data in component state with indices

As a practice project, I've started building a small Pokedex app in React.
import React, { Component} from 'react';
import './App.css';
import Card from './components/card/Card.component';
class App extends Component{
constructor(){
super();
this.state = {}
}
componentDidMount(){
let pokeDataArr = []
const getPokemonData = async() => {
const dataResponse = await fetch(
'https://pokeapi.co/api/v2/pokemon?limit=10'
);
const dataArr = await dataResponse.json();
const dataArr2 = await dataArr.results.forEach(i => {
fetch(i.url)
.then(dataResponse => dataResponse.json())
.then(json => pokeDataArr.push(json))
})
this.setState({ pokeDataArr }, () => console.log(this.state))
}
getPokemonData();
}
render(){
return(
<div>Pokedex!</div>
)
}
}
I'm having trouble accessing data from a specific index in an array.
When I log the entire state object to the console, I can see all the data I have retrieved from the AJAX call.
this.setState({ pokeDataArr }, () => console.log(this.state))
And this is the result in the console:
console result
However, if I try to log out data from an index in the array with:
this.setState({ pokeDataArr }, () => console.log(this.state.pokeDataArr[0]))
I get "undefined" in the console:
console result 2
As far as I'm aware, whatever function you run in the this.setState method's callback, it should run after setState has finished.
My goal is to use the data from this.state.pokeDataArr to make cards that display the info of each individual pokemon, but it seems like I'm stuck until I find a way to extract the data from the array and I have no clue what I'm missing.
Thank you for your time.
I think you messed up with your react state.
Usually, what people do is they set up their react state as an object with other elements (arrays, objects, strings, whatever) inside it. This looks something like this:
constructor(){
super();
this.state = {
myObject: {},
somethingElse: "",
anArray: []
}
}
This enables you to access parts of your state like this: this.state.myObject for instance. (this would return {})
In your example, you defined your state as an empty object.
constructor(){
super();
this.state = {}
}
And later, you set this object to an object, with an array inside:
this.setState({ pokeDataArr });
This will set your state to this: {[(your array)]}
To prevent this initialize your state like this:
constructor(){
super();
this.state = { pokeDataArr : {} }
}
And set your values like this:
this.setState({ pokeDataArr: pokeDataArr }, () => console.log(this.state.pokeDataArr[0]))
read more here: https://reactjs.org/docs/state-and-lifecycle.html
You'll need to use updater to use the callback instead of plain state update:
this.setState(
() => ({ pokeDataArr }),
() => console.log(this.state.pokeDataArr[0])
)
Read the note from the docs in the linked example:
Subsequent calls will override values from previous calls in the same cycle, so the quantity will only be incremented once. If the next state depends on the current state, we recommend using the updater function form

How to delay parent component from rendering until a setState in child component has finished?

**parent-component**
class MatchVideos extends Component {
DaySelector = createRef(); //creating ref for child-component
componentDidMount() {
utils.getData().then((response) => {
this.setState({ dayData: response.data }, () => {
this.getData(this.DaySelector.current.state.currentDay); //calling a method in parent by passing child-component data
});
})
.catch(err => {
if (err.response) {
this.setState({ isError: true })
}
})
}
render(){
return(
<DaySelector ref={this.DaySelector}/>
)
}
}
**child-component(DaySelector)**
componentDidMount(){
let data = {
"trn_year": utils.trn_year,
"trn_id": utils.trn_id
}
utils.getDay(data).then((response) =>{
this.dates = response.data.dayWise;
var curDay = this.dates.filter(function(day) {
console.log(new Date().toISOString().split('T')[0]);
return( day.Date === "2019-01-19"
)
})
this.setState({currentDay: curDay[0].Day, selectedDay: curDay[0].Day}, ()=>{ //this is the state I want to access in parent-component.
utils.selected_day = this.state.selectedDay;
utils.selected_date = curDay[0].Date;
});
})
}
So we know componentDidMount of parent will be executed only after all the child components componentDidMount methods are executed. But my issue is 1 in 5 times the ref of Dayselector in parent method is empty(current: null). As you can see I'm using the state of child component(Day selector) in parent component. I think the issue is the setState in child method is taking some time, and the parent method meanwhile gets executed with null values. How to solve this issue? And correct me if I'm wrong or completely off-point in my question. All I need is the parent component should render after there is data in child-component.
I'm showing only the necessary code for understanding my question
Hey React have these cool thing Called React lazy and React Suspense
For usage you check this cool video video from Siddharth Kshetrapal

How to update state of component in componentDidUpdate() without being stuck in an infinite re render?

I have a component with a componentDidMount() method that calls a method called getData() which gets the initial data and sets the initial state of the component.
class LogsSettings extends React.Component {
constructor(props) {
super(props);
this.settingsUrls = [
"/ui/settings/logging"
];
this.state = {
configSettings: {},
formSchema: formSchema
};
this.configSettings = {};
this.selected = "general";
}
getData = (url, selectedSetting) => {
fetch(url)
.then((response) => {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
response.json().then((response) => {
//pass formschema here
console.log(selectedSetting);
let newFormSchema = this.setNonDefaultValues(response.data, formSchema.subsections);
Object.assign(this.configSettings, response.data);
this.setState({
configSettings : this.configSettings,
formSchema: newFormSchema
});
});
}
)
.catch((err) => {
console.log('Fetch Error :-S', err);
});
};
componentDidMount() {
this.settingsUrls.map((settingUrl) => {
this.getData(settingUrl, this.selected)
})
}
componentDidUpdate() {
this.settingsUrls.map((settingUrl) => {
this.getData(settingUrl, this.props.selectedSetting)
})
}
render() {
return (
<div className="card-wrapper">
<h2>{formSchema.label.toUpperCase()}</h2>
{
formSchema.subsections.map((subSection) => {
return (
<>
<h3>{subSection['description']}</h3>
<div style={{marginBottom: '10px'}}></div>
{
subSection['input_fields'].map((inputField) => {
return buildForm(inputField, this.handleChange)
})
}
<hr></hr>
</>
)
})
}
<button className="button button-primary">Save Changes</button>
</div>
)
}
}
The selectedSetting parameter that gets passed to the getData() method in this component will change however and when this changes, I need to change the state of the component and get new data specific to the changed selectedSetting parameter.
The new selectedSetting is passed into the component as a prop. The problem is that I can't pass the new selectedSetting parameter to my getData method to update the state of the component as it gets caught in an infinite loop.
How do I go about passing the new selectedSetting to the getData() method without getting caught in an infinite loop? Is this even possible? If not, what is the best approach I should take?
note the selectedSetting parameter isn't used in the getData() function yet but will be and it will be used to get data from an API call and a new form schema which will then lead to the ConfigSettings and formSchema states being changed
If you look closely on the lifecycle of your component, after mount, you'll fetch then update the component. This will trigger the componentDidUpdate lifecycle method which will do the same thing, causing the infinite loop. You need to have a flag that checks whether this.props.selected changed. If it didn't, don't fetch the data else fetch as normal. In the update method, you have access to the previous props. (You may also do this in componentShouldUpdate method, but it'll be just outright risky)
componentDidUpdate(prevProps) {
if( prevProps.selectedSetting !== this.props.selectedSetting ){
this.settingsUrls.map((settingUrl) => {
this.getData(settingUrl, this.props.selectedSetting)
})
}
}
also just a heads up, I noticed that your didMount method, uses a default of "general" as the selected setting, since you want to be using this.props.selectedSetting might be better if it was the one being used instead and just set default props to "general".

How to stop function again and again calling in render

I am new to react, I am getting data from redux, first, I get an object from accounts from redux, then I pass this to the function in redux and set a value in numReg in the reducer.
When I call a function by this.props.fetchAccountDetail(data) in actions its send a request to API and fetch the data from API and save it in reducer or store. When i call function in render by
this.getDataFromAccount(accountDetail.num), it goes in infinite loop.
I want data in a return, it should only run one time.
import React, { Component } from 'react'
import { fetchAccountDetail, } from '../../../actions'
class myclass extends Component {
state = {
num : ''
};
getAccounts = (data) => {
if (!data) { return; }
return data.find(item => item.id == this.props.match.params.id);
}
getDataFromAccount = (data) => {
this.props.fetchAccountDetail(data);
// This is a api , which provide the result agaisnt
// a num and set value in numReg in reducer
}
render() {
const { accounts, numReg } = this.props;
const accountDetail = this.getAccounts(accounts);
// Here i will get a match object like {id :1 , num :12345}
const test=this.getDataFromAccount(accountDetail.num)
// When i call this , it stucks in infinite loop , how can i get data only once when it render
console.log(test)
return (
<div />
);
}
}
const mapStateToProps = state => {
return { accounts : state.accounts.accounts | [{id :1 , num :12345} , {id :2 , num :535234}],
numReg : state.accounts.numReg
//Is a object containg the information of num from accounts
}
}
export default (compose(
withStyles(styles),
connect(mapStateToProps, { fetchAccountDetail,}))(myclass));
It should return data in variable test after fetching data from redux.
You should never call data fetching functions or functions which alter the state within render.
Render may be called multiple times if a parent rerenders or just its internal state changes.
Calling fetchAccountDetails in render updates the redux store. Redux will pass the new but equal data as props into your component.
That Component will rerender because its props changed and will call fetchAccountDetails again => loop. Render should only display data!!
For data fetching, 2 functions exist. componentDidMount which will be called after the component is visible. That would be a good place to call your fetch.
If you need a prop to fetch the data for e.g. an Id of some sort (fetch data for that Id), you would use componentDidUpdate in which you compare the new id and the old id to see if you need to fetch the data again.
You should read the docs and look at some tutorials.
Hope this helps.
Happy coding.
As Domino987 answered, you need to make use of lifecycle methods. Here's an example of how it might look:
componentDidMount() {
const { accounts } = this.props;
const accountDetail = this.getAccounts(accounts);
const accountData = this.getDataFromAccount(accountDetail.num)
this.setState({
account: {
accountDetail: accountDetail,
accountData: accountData
}
})
}
componentDidUpdate() {
const { accounts } = this.props;
const accountDetail = this.getAccounts(accounts);
const accountData = this.getDataFromAccount(accountDetail.num)
if (this.state.account.accountData !== this.getDataFromAccount(accountDetail.num)) {
this.setState({
account: {
accountDetail: accountDetail,
accountData: accountData
}
})
}
}

How to get data from server repeatedly in React + Redux application?

I am developing React + Redux single page application. I have a table with documents in page and I need to refresh data every 20 seconds. There are two functions in javascript: setTimeout and setInterval. I guess I can't use setInterval, because it just call function after some period of time. In my case I need to call function and wait for response (request processing in backend takes some time). So I used setTimeout and wrote this component (now it's simplified):
import {connect} from 'react-redux';
const { DATA_REFRESH_TIMEOUT, RETRY_REFRESH_TIMEOUT } = __ENVIRONMENT_CONFIG__;
#connect(
(state) => ({
documents: state.documents.documents,
loadingDocuments: state.documents.loading
}),
(dispatch) => bindActionCreators(
{
dispatchLoadDocuments: loadDocuments
},
dispatch
)
)
export default class Dashboard extends Component {
documentasTimeoutId;
constructor(props) {
super(props);
this.state = {
documentType: null
};
}
....
handleDocumentTypeChange = (event, documentType) => {
//If document type was changed I must to abort current timer
//and get documents with particular type immediately
this.setState({documentType: documentType});
this.clearTimeoutAndGetDocuments(documentType);
};
getDocuments = (documentType) => {
//Here I am checking for document loading phase
//If it is loading, we will wait and repeat loading after short time
const{ loadingDocuments } = this.props;
if(!loadingDocuments) {
this.props.dispatchLoadDocuments(documentType);
} else {
this.documentasTimeoutId = setTimeout(() => { this.getDocuments(documentType); }, RETRY_REFRESH_TIMEOUT);
}
};
clearTimeoutAndGetDocuments = (documentType) => {
//Abort delayed data getting and get data immediately
clearTimeout(this.documentasTimeoutId);
this.getDocuments(documentType);
};
componentDidMount(){
//Load documents on start up
this.props.dispatchLoadDocuments();
}
componentWillReceiveProps(newProps) {
//Here I trying to get event when documents loaded
let areDocumentsJustLoaded = this.props.loadingDocuments && !newProps.loadingDocuments;
if(areDocumentsJustLoaded) {
//If they loaded, I am setting timeout to refresh documents after some time
this.documentasTimeoutId = setTimeout(() => { this.getOutstandingFailures(this.state.search); }, DATA_REFRESH_TIMEOUT);
}
}
render() {
const {columns, documents} = this.props;
return (
//.....
<DataTable
columns={columns}
source={documents}
title="Documents"
name="documents"
emptyMessage="No data"/>
//....
);
}
}
As you can see I'm getting documents and loadingDocuments from reducer. I put documents to my DataTable, and by loadingDocuments changes I can define when data loading completed.
It's working, but I'am not sure for correct react and redux using (I am a newbie in React/Redux). Maybe there a better approach to do same actions? Maybe we can somehow create a separate component for this purpose and reuse it in other pages?

Categories

Resources