I'm currently fetching data in Component1, then dispatching an action to update the store with the response. The data can be seen in Component2 in this.props, but how can I render it when the response is returned? I need a way to reload the component when the data comes back.
Initially I had a series of functions run in componentDidMount but those are all executed before the data is returned to the Redux store from Component1. Is there some sort of async/await style between components?
class Component1 extends React.Component {
componentDidMount() {
this.retrieveData()
}
retrieveData = async () => {
let res = await axios.get('url')
updateParam(res.data) // Redux action creator
}
}
class Component2 extends React.Component {
componentDidMount() {
this.sortData()
}
sortData = props => {
const { param } = this.props
let result = param.sort((a,b) => a - b)
}
}
mapStateToProps = state => {
return { param: state.param }
}
connect(mapStateToProps)(Component2)
In Component2, this.props is undefined initially because the data has not yet returned. By the time it is returned, the component will not rerender despite this.props being populated with data.
Assuming updateParam action creator is correctly wrapped in call to dispatch in mapDispatchToProps in the connect HOC AND properly accessed from props in Component1, then I suggest checking/comparing props with previous props in componentDidUpdate and calling sortData if specifically the param prop value updated.
class Component2 extends React.Component {
componentDidMount() {
this.sortData()
}
componentDidUpdate(prevProps) {
const { param } = this.props;
if (prevProps.param !== param) { // <-- if param prop updated, sort
this.sortData();
}
}
sortData = () => {
const { param } = this.props
let result = param.sort((a, b) => a - b));
// do something with result
}
}
mapStateToProps = state => ({
param: state.param,
});
connect(mapStateToProps)(Component2);
EDIT
Given component code from repository
let appointmentDates: object = {};
class Appointments extends React.Component<ApptProps> {
componentDidUpdate(prevProps: any) {
if (prevProps.apptList !== this.props.apptList) {
appointmentDates = {};
this.setAppointmentDates();
this.sortAppointmentsByDate();
this.forceUpdate();
}
}
setAppointmentDates = () => {
const { date } = this.props;
for (let i = 0; i < 5; i++) {
const d = new Date(
new Date(date).setDate(new Date(date).getDate() + i)
);
let month = new Date(d).toLocaleString("default", {
month: "long"
});
let dateOfMonth = new Date(d).getDate();
let dayOfWeek = new Date(d).toLocaleString("default", {
weekday: "short"
});
// #ts-ignore
appointmentDates[dayOfWeek + ". " + month + " " + dateOfMonth] = [];
}
};
sortAppointmentsByDate = () => {
const { apptList } = this.props;
let dates: string[] = [];
dates = Object.keys(appointmentDates);
apptList.map((appt: AppointmentQuery) => {
return dates.map(date => {
if (
new Date(appt.appointmentTime).getDate().toString() ===
// #ts-ignore
date.match(/\d+/)[0]
) {
// #ts-ignore
appointmentDates[date].push(appt);
}
return null;
});
});
};
render() {
let list: any = appointmentDates;
return (
<section id="appointmentContainer">
{Object.keys(appointmentDates).map(date => {
return (
<div className="appointmentDateColumn" key={date}>
<span className="appointmentDate">{date}</span>
{list[date].map(
(apptInfo: AppointmentQuery, i: number) => {
return (
<AppointmentCard
key={i}
apptInfo={apptInfo}
/>
);
}
)}
</div>
);
})}
</section>
);
}
}
appointmentDates should really be a local component state object, then when you update it in a lifecycle function react will correctly rerender and you won't need to force anything. OR since you aren't doing anything other than computing formatted data to render, Appointments should just call setAppointmentDates and sortAppointmentsByDate in the render function.
Related
I am using react-star package
import ReactStars from "react-rating-stars-component";
It has one issue.
I need to the value to change on state change. But the value is not changing
I am changing the this.state.rating on ajax load.
and setting the value this.rating to be used in submit.
class CallUpdate extends Component<{ match: PropsWithRef<any> }> {
state = {
rating:0
}
rating = 0;
componentDidMount = async () => {
this.id = this.props.match.params.id;
const userCall = await axios.get(`call/show/${this.id}`);
const call: call= userCall.data.data;
this.setState({
rating: call.rating
});
}
submit = async (e: SyntheticEvent) => {
e.preventDefault();
const formData = new FormData();
formData.append('rating', this.rating);
//const formHeaders['Content-Type'] = 'multipart/form-data';
const config = {headers: {'Content-Type' :'multipart/form-data'}};
await axios.post(`call/${this.id}/update`, formData,config);
}
render() {
return (
<ReactStars
count={5}
value={this.state.rating}
onChange={(rating) => {this.rating = rating}}
size={24}
activeColor="#ffd700"
/>
);
}
}
export default CallUpdate;
////
react-starts-component I have added this function. it should be called on props change.
function updateValue(value){
if (value < 0 || value > count) {
setCurrentValue(0);
}
else {
setCurrentValue(value);
}
}
I tried changing the useEffect
useEffect(() => {
addClassNames();
validateInitialValue(props.value, props.count);
setStars(getStars(props.value));
setConfig(props);
createUniqueness();
setIsUsingIcons(iconsUsed(props));
setHalfStarAt(Math.floor(props.value));
setHalfStarHidden(props.isHalf && props.value % 1 < 0.5);
}, []);
to
useEffect(() => {
addClassNames();
validateInitialValue(props.value, props.count);
setStars(getStars(props.value));
setConfig(props);
createUniqueness();
setIsUsingIcons(iconsUsed(props));
setHalfStarAt(Math.floor(props.value));
setHalfStarHidden(props.isHalf && props.value % 1 < 0.5);
}, [props]);
But on state.rating change it is not updating the value.
How do I change the code to make it property change value?
The issue here is you are not updating the state but instead updating a property on class which doesn't cause a re-render and you are using lead whereas you have a call variable in componentDidMount which doesn't exists. You should use a state variable to hold data that might change overtime and reflect in UI.
class CallUpdate extends Component {
state = {
rating: 0
};
componentDidMount = async () => {
const id = this.props.match.params.id;
const userCall = await axios.get(`call/show/${this.id}`);
const call: call = userCall.data.data;
this.setState({
rating: call.rating
});
};
submit = async (e: SyntheticEvent) => {
e.preventDefault();
const formData = new FormData();
formData.append("rating", this.rating);
//const formHeaders['Content-Type'] = 'multipart/form-data';
const config = { headers: { "Content-Type": "multipart/form-data" } };
await axios.post(`call/${this.id}/update`, formData, config);
};
render() {
return (
<ReactStars
count={5}
value={this.state.rating}
onChange={(rating) => this.setState({rating})}
size={24}
activeColor="#ffd700"
/>
);
}
}
I had the same problem when value not updating from useMemo(). You just need to add key prop to ReactStars: key={value}
I am trying to populate a dropdown with values from API. I declared empty array in react class but cannot assign the values to it. I cannot use it as state variables as I have to make lot of changes to previously developed code. The way I did the code it says options is not defined.
The partial code is posted below which is causing the problem. Any help is really appreciated.
export default class LoadLimits extends Component {
constructor(props){
super(props);
this.options = []
this.getZoneOptions = this.getZoneOptions.bind(this)
}
render(){
return (
<PtSelect label="Assigned Zone" options={options}
onChange={this.onChangeDropdown}
disabled={this.props.disabled}
defaultVal={this.state.assignedZone} name="assignedZone" />
)
}
getZoneOptions = () =>{
const zoneOptions = []
const keys = []
fetch(`${config.server}/getzoneOptions/`+this.props.ownModel.agencyId)
.then(response=>
{
return response.json();
})
.then(data=>{
for (var i =0;i<data[0].length;i++){
if (data[0][i]['Zone_key']!==998){
zoneOptions.push(data[0][i]['description'])
keys.push(data[0][i]['Zone_key'])
}
}
let dropOptions = zoneOptions.map((option,idx)=>{
return {key:keys[idx],value: option, label:option}
});
this.options = dropOptions
})
.catch(error=>{
console.log(error);
});
}
}
Issue
The options being passed to PtSelect is not defined.
<PtSelect
label="Assigned Zone"
options={options} // <-- should be this.options
onChange={this.onChangeDropdown}
disabled={this.props.disabled}
defaultVal={this.state.assignedZone}
name="assignedZone"
/>
Solution
If you need a variable to hold a value that you don't want coupled to the React component lifecycle then you should probably use a React ref.
Import createRef from 'react'.
Create a mutable ref for the options.
Implement the componentDidMount lifecycle method to populate and set the current value of the options.
Pass the current options value to the PtSelect component.
Code
import React, { Component, createRef } from 'react';
class LoadLimits extends Component {
constructor(props) {
super(props);
this.getZoneOptions = this.getZoneOptions.bind(this);
this.options = createRef([]);
}
getZoneOptions = () => {
const zoneOptions = [];
const keys = [];
fetch(`${config.server}/getzoneOptions/` + this.props.ownModel.agencyId)
.then((response) => {
return response.json();
})
.then((data) => {
for (var i = 0; i < data[0].length; i++) {
if (data[0][i]["Zone_key"] !== 998) {
zoneOptions.push(data[0][i]["description"]);
keys.push(data[0][i]["Zone_key"]);
}
}
const dropOptions = zoneOptions.map((option, idx) => {
return { key: keys[idx], value: option, label: option };
});
this.options.current = dropOptions;
})
.catch((error) => {
console.log(error);
});
};
componentDidMount() {
this.getZoneOptions();
}
render() {
return (
<PtSelect
label="Assigned Zone"
options={this.options.current}
onChange={this.onChangeDropdown}
disabled={this.props.disabled}
defaultVal={this.state.assignedZone}
name="assignedZone"
/>
);
}
}
Alternative Solution - Use forceUpdate (not strongly suggested)
In addition to addressing the this.options issue in PtSelect, you can use forceUpdate to tell React to rerender regardless of any state and/or prop update. This should rerender the select with populated options.
component.forceUpdate(callback)
By default, when your component’s state or props change, your
component will re-render. If your render() method depends on some
other data, you can tell React that the component needs re-rendering
by calling forceUpdate().
Calling forceUpdate() will cause render() to be called on the
component, skipping shouldComponentUpdate(). This will trigger the
normal lifecycle methods for child components, including the
shouldComponentUpdate() method of each child. React will still only
update the DOM if the markup changes.
Normally you should try to avoid all uses of forceUpdate() and only
read from this.props and this.state in render().
getZoneOptions = () => {
const zoneOptions = [];
const keys = [];
fetch(`https://jsonplaceholder.typicode.com/users`)
.then((response) => {
return response.json();
})
.then((data) => {
for (var i = 0; i < data.length; i++) {
zoneOptions.push(data[i]["name"]);
keys.push(data[i]["id"]);
}
let dropOptions = zoneOptions.map((option, idx) => {
return { key: keys[idx], value: option, label: option };
});
this.options = dropOptions;
console.log("Options ", this.options);
this.forceUpdate(); // <-- trigger a rerender
})
.catch((error) => {
console.log(error);
});
};
Basically, I have one component, let's call it component1 and a second component, which has been created by duplicating the first one called component2. I had to duplicate it, because some objects inside it had to be altered before sending them to the further components.
On one page I have an onClick event which triggers component1 which opens a modal and on another page, component2 is trigger the same as for the first one.
The problem occurs here, if I'm on the second page where the modal from component2 is opened and I refresh the page, both components are called, of course component1 is the first one called and the state is altered by this component which makes me not having the desired information in the second component.
As far as I understood, because of the fact that in both components, mapStateToProps is altering my state, both components are called. Not really sure though that I understood right.
Here is my component1 summary:
class LivePlayerModal extends React.Component {
constructor(props) {
super(props);
this.highlightsUpdated = null;
}
componentDidMount() {
const queryParam = UrlHelper.getParamFromLocation(IS_QUALIFICATION, window.location);
if (queryParam === null) {
ScoringLoader.subscribe(endpointNames.LIVE_SCANNER);
ScoringLoader.subscribe(endpointNames.PLAYERS);
ScoringLoader.subscribe(endpointNames.LEADERBOARD);
ScoringLoader.subscribe(endpointNames.COURSE);
ScoringLoader.subscribe(endpointNames.STATISTICS);
}
//TODO: make fixed fetch on timeout
this.fetchHighlights();
}
componentDidUpdate(prevProps) {
if (prevProps.playerId !== this.props.playerId) {
this.highlightsUpdated = null;
}
this.fetchHighlights();
}
componentWillUnmount() {
ScoringLoader.unsubscribe(endpointNames.LIVE_SCANNER);
ScoringLoader.unsubscribe(endpointNames.PLAYERS);
ScoringLoader.unsubscribe(endpointNames.LEADERBOARD);
ScoringLoader.unsubscribe(endpointNames.COURSE);
ScoringLoader.unsubscribe(endpointNames.STATISTICS);
}
render() {
const {
isOpen, scoringPlayer, isQualification, ...rest
} = this.props;
const highlightGroups = getHighlights(this.getCloudHighlights());
if (isQualification) {
return null;
}
return (
<ReactModal isOpen={isOpen} onCloseCb={this.hide}>
<div className="live-player">
{
scoringPlayer === undefined &&
<BlockPlaceholder minHeight={400}>
<BlockSpinner />
</BlockPlaceholder>
}
{
scoringPlayer === null &&
<LivePreMessage
model={{
title: '',
body: 'Player data coming soon'
}}
bemList={[bemClasses.LIGHT]}
/>
}
{
scoringPlayer &&
<LivePlayerLayout
{...rest}
scoringPlayer={scoringPlayer}
highlightGroups={highlightGroups}
/>
}
</div>
</ReactModal>
);
}
}
const mapStateToProps = (state, ownProps) => {
const isQualification = state.scoring.isQualification;
const { playerId } = ownProps;
const sitecorePlayers = state.scoring[endpointNames.PLAYERS];
const scoringLeaderboard = state.scoring[endpointNames.LEADERBOARD];
const getScoringPlayer = () => {
};
return ({
isQualification,
liveScanner: state.scoring[endpointNames.LIVE_SCANNER],
scoringLeaderboard,
scoringPlayer: getScoringPlayer(),
scoringStats: state.scoring[endpointNames.STATISTICS],
scoringCourse: state.scoring[endpointNames.COURSE],
sitecorePlayers: state.scoring[endpointNames.PLAYERS],
cloudMatrix: state.cloudMatrix
});
};
const mapDispatchToProps = (dispatch) => ({
fetchPlayerHighlights: (feedUrl) => dispatch(fetchFeed(feedUrl))
});
const LivePlayerCardContainer = connect(
mapStateToProps,
mapDispatchToProps
)(LivePlayerModal);
export default LivePlayerCardContainer;
Here is my component2 summary :
class QualificationLivePlayerModal extends React.Component {
constructor(props) {
super(props);
this.highlightsUpdated = null;
}
shouldComponentUpdate(nextProps) {
return nextProps.isQualification;
}
componentDidMount() {
ScoringLoader.subscribe(endpointNames.SUMMARY_FINAL);
ScoringLoader.subscribe(endpointNames.SUMMARY_REGIONAL);
ScoringLoader.subscribe(endpointNames.LIVE_SCANNER);
ScoringLoader.subscribe(endpointNames.PLAYERS);
ScoringLoader.subscribe(endpointNames.COURSE);
ScoringLoader.unsubscribe(endpointNames.LEADERBOARD);
ScoringLoader.unsubscribe(endpointNames.STATISTICS);
//TODO: make fixed fetch on timeout
this.fetchHighlights();
}
componentDidUpdate(prevProps) {
if (prevProps.playerId !== this.props.playerId) {
this.highlightsUpdated = null;
}
this.fetchHighlights();
}
componentWillUnmount() {
ScoringLoader.unsubscribe(endpointNames.SUMMARY_FINAL);
ScoringLoader.unsubscribe(endpointNames.SUMMARY_REGIONAL);
ScoringLoader.unsubscribe(endpointNames.COURSE);
ScoringLoader.unsubscribe(endpointNames.LEADERBOARD);
ScoringLoader.unsubscribe(endpointNames.STATISTICS);
}
render() {
const {
scoringPlayer, summaryFinal, ...rest
} = this.props;
const highlightGroups = getHighlights(this.getCloudHighlights());
const queryParam = UrlHelper.getParamFromLocation(IS_QUALIFICATION, window.location);
const open = (queryParam === 'true');
if (scoringPlayer !== undefined && scoringPlayer !== null) scoringPlayer.id = scoringPlayer.entryId;
return (
<ReactModal isOpen={open} onCloseCb={this.hide}>
<div className="qual-live-player">
{
scoringPlayer === undefined &&
<BlockPlaceholder minHeight={400}>
<BlockSpinner />
</BlockPlaceholder>
}
{
scoringPlayer === null &&
<LivePreMessage
model={{
title: '',
body: 'Player data coming soon'
}}
bemList={[bemClasses.LIGHT]}
/>
}
{
scoringPlayer &&
<LivePlayerLayout
{...rest}
scoringPlayer={scoringPlayer}
highlightGroups={highlightGroups}
/>
}
</div>
</ReactModal>
);
}
}
const mapStateToProps = (state, ownProps) => {
const isQualification = state.scoring.isQualification;
const { playerId, location } = ownProps;
const locationIdFromQueryParam = UrlHelper.getParamFromLocation(LOCATION_ID, window.location);
const locationId = location !== null ? location.locationId : locationIdFromQueryParam;
const sitecorePlayers = state.scoring[endpointNames.PLAYERS];
const summaryRegional = state.scoring[endpointNames.SUMMARY_REGIONAL];
const summaryFinal = state.scoring[endpointNames.SUMMARY_FINAL];
const scoringLeaderboard = getLeaderboardBasedOnLocation(locationId, summaryFinal, summaryRegional);
const currentRound = getCurrentRound(locationId, summaryFinal, summaryRegional);
const getScoringPlayer = () => {
};
return ({
isQualification,
liveScanner: state.scoring[endpointNames.LIVE_SCANNER],
scoringLeaderboard,
scoringPlayer: getScoringPlayer(),
scoringCourse: getScoringCourseFromQualificationFeed(),
sitecorePlayers: state.scoring[endpointNames.PLAYERS],
cloudMatrix: state.cloudMatrix,
});
};
const mapDispatchToProps = (dispatch) => ({
fetchPlayerHighlights: (feedUrl) => dispatch(fetchFeed(feedUrl))
});
const QualificationLivePlayerCardContainer = connect(
mapStateToProps,
mapDispatchToProps
)(QualificationLivePlayerModal);
export default QualificationLivePlayerCardContainer;
Basically, the problem i ve got here, is that in state.scoring I do not have the information for the endpoints present in the return statement of the render method before the page finishes the refresh process, which later on makes my app to break.
Hope I've been clear enough.
Is there a solution for waiting the endpoints to get called or even not loading the first component at all?
I am fetching data from an API. I am building an array of 5 objects using the API call. What I am trying to do is iterate over the array, use the data inside each array index to build a component and pass along the props to another component.
I've tried accessing the element the same way I normally would by doing:
img={pokemon.name} but it keeps returning undefined. When I type in
console.log(pokemon) I get the individual pokemon stored within the array of objects.
import React, { Component } from "react";
import Pokecard from "./Pokecard";
async function getPokemon() {
const randomID = Math.floor(Math.random() * 151) + 1;
const pokeRes = await fetch(`https://pokeapi.co/api/v2/pokemon/${randomID}/`);
const pokemonJSON = await pokeRes.json();
return pokemonJSON;
}
function buildPokemon() {
let pokemonArr = [];
let builtPokemon = {};
getPokemon()
.then(data => {
builtPokemon.name = data.forms[0].name;
builtPokemon.exp = data.base_experience;
builtPokemon.img = data.sprites.front_default;
builtPokemon.type = data.types[0].type.name;
pokemonArr.push(builtPokemon);
})
.catch(err => console.log(err));
return pokemonArr;
}
class Pokedex extends Component {
constructor(props) {
super(props);
this.state = { pokemonArr: [] };
}
componentDidMount() {
const pokemonArr = [];
for (let i = 0; i < 5; i++) {
pokemonArr.push(buildPokemon());
}
this.setState({ pokemonArr });
}
render() {
console.log(this.state.pokemonArr);
return (
<div className="Pokedex">
{this.state.pokemonArr.map(pokemon => console.log(pokemon))}
</div>
);
}
}
export default Pokedex;
What should happen is that when I map the pokemonArr I want to create 5 separate pokemon by doing
this.state.pokemonArr.map(pokemon => <Pokecard name={pokemon.name} but I keep getting undefined whenever I check this.props in the Pokecard component.
I think my buildPokemon() function is working because when I call it in the componentDidMount() and then I console.log this.state.pokemonArr in the render() function, I actually get an array returned with 5 different pokemon with the proper fields filled out.
And also when I map out this.state.pokemonArr.map(pokemon => clg(pokemon)), it actually displays each individual pokemon. When I pass the pokemon item into a component like this
<Pokecard name={pokemon}/>, I see all the pokemon data.
when I type <Pokecard name={pokemon.name} I get undefined
There are several problems with your approach but the main one is that getPokemon() is asynchronous.
Return the getPokemon() promise from buildPokemon() and return the object from it's then()
In your for() loop create an array of these promises and use Promise.all() to set state once they have all resolved
function buildPokemon() {
let builtPokemon = {};
// return the promise
return getPokemon()
.then(data => {
builtPokemon.name = data.forms[0].name;
builtPokemon.exp = data.base_experience;
builtPokemon.img = data.sprites.front_default;
builtPokemon.type = data.types[0].type.name;
// return the object
return builtPokemon
});
}
componentDidMount() {
const pokemonPromises = [];
for (let i = 0; i < 5; i++) {
pokemonPromises.push(buildPokemon());
}
Promise.all(pokemonPromises).then(pokemonArr => this.setState({ pokemonArr }));
}
componentDidMount executes after first render, initially your state is pokemonArr: [] (whch is empty) so you are getting an error. You need to conditionally render like,
{this.state.pokemonArr.length > 0 && this.state.pokemonArr.map(pokemon => console.log(pokemon))}
Side Note:
In buildPokemon function you are returning an array, and again in componentDidMount you are storing it in array which creates array of array's, you just need to return object from buildPokemon function.
The problem is mainly how the Promise should be resolved.
The data isn't available right away so the state (pokemonArr) should only be set once data is available.
Here's the refactored component:
class Pokedex extends Component {
constructor(props) {
super(props);
this.state = { pokemonArr: [] };
}
componentDidMount() {
for (let i = 0; i < 5; i++) {
this.getPokemon()
.then((pokemon) => this.buildPokemon(pokemon));
}
}
async getPokemon() {
const randomID = Math.floor(Math.random() * 151) + 1;
const pokeRes = await fetch(`https://pokeapi.co/api/v2/pokemon/${randomID}/`);
return pokeRes.json();
}
setPokemon(pokemon) {
this.setState({
pokemonArr: [
...this.state.pokemonArr, pokemon
],
});
}
buildPokemon(data) {
let builtPokemon = {};
builtPokemon.name = data.forms[0].name;
builtPokemon.exp = data.base_experience;
builtPokemon.img = data.sprites.front_default;
builtPokemon.type = data.types[0].type.name;
this.setPokemon(builtPokemon);
}
render() {
return (
<div className="Pokedex">
{this.state.pokemonArr.map(pokemon => console.log(pokemon))}
</div>
);
}
}
I'd like to fire interval after backend data will come in as a prop.
Take a look at this chunk of code:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getAdvert } from "../../actions/advert";
import './_advert.scss';
class Advert extends Component {
state = { counter: 0 };
componentDidMount() {
const { getAdvert } = this.props;
getAdvert();
}
componentWillReceiveProps(nextProps) {
const { counter } = this.state;
this.bannerInterval = setInterval(() => {
this.setState({ counter: counter === Object.keys(nextProps.banners).length - 1 ? 0 : counter + 1 });
}, 1000)
}
render() {
const { banners } = this.props;
const { counter } = this.state;
return (
<div className="advert__container">
<img src={banners[counter] && banners[counter].image_url} alt="advert" />
</div>
);
}
}
const mapStateToProps = ({ banners }) => {
return { banners };
};
const mapDispatchToProps = (dispatch) => {
return {
getAdvert: () => dispatch(getAdvert())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Advert);
So as you can see I tried to run it within componentWillReceiveProps method as I thought it might be proper place to be dependent on incoming props. But that won't work. I will run only once and interval will be repeating the same value.
Thanks for helping!
ComponentWillReceive props is extremely dangerous used in this way. You are istantiating a new interval every time a props is received without storing and canceling the previous one.
Also is not clear how do you increment counter, as I see the increment in your ternary will not increase the counter value incrementaly.
// This is an example, the lifeCylce callback can be componentWillReceive props
componentDidMount() {
const intervalId = setInterval(this.timer, 1000);
// store intervalId in the state so it can be accessed later:
this.setState({intervalId: intervalId});
}
componentWillUnmount() {
// use intervalId from the state to clear the interval
clearInterval(this.state.intervalId);
}
timer = () => {
// setState method is used to update the state with correct binding
this.setState({ currentCount: this.state.currentCount -1 });
}