I have a component A that gets mounted when the user visits my HomePage.
In its ComponentDidMount() method, I fetch data from a database that gets display on screen.
class A extends React.Component{
...
componentDidMount(){
this.setState({
///set States
}, function() {axios.get(
// get params
)})
}
...
}
Then I click a link which redirects me to another page of Compnent B whilst sending my state in props, so the state from Component A does not get lost.
When I return to my home page of component A, componentDidMount fetches these data again.
Though this is not a mistake, it does create an additional database request which hinders performance.
How can I call componentDidMount() conditionally?
Related
I have a problem with ReactJS as when in parent component's state that stores child components (as thumbnails) the child components stored as array of components are constructed once and have componentDidMount called once. Once I 'reimport' data and create new child components based on new data from backend API (for example upon new sorting mode activated) the components do not call componentDidMount and i have to rely on componentWillReceiveProps method to import for example a link to the picture to be displayed on a thumbnail (it seems like react reuses components). If for example the the data in child components is being imported slowly it shows and old photo because remembers previous iteration done in own componentDidMount done after creation.
How can i force react to always create new child components from the scratch and thanks to that achieve having componentDidMount called to include data import from backend and avoid relying on componentWillReceiveProps call?
Here is the pseudocode where parent component ComponentManager imports person data from backend and creates thumbnails based on retrieved JSON. Thenafter it can upodate thumbnails after user changes sorting order:
class ComponentManager extends Component {
constructor(props) {
super(props);
this.state = {
personsThumbnails : undefined
}
}
componentDidMount() {
// Import person ids and create SinglePersonThumbnail(s) child components as the state personsThumbnails
importPersonsIds();
}
importPersonsIds(sortingMode) {
// Importing persons data from backend API and created thumbnails stored in personsThumbnails state
...
}
render() {
return(
<div>
<button onClick={()=>{this.importPersonsIds("SORT_BY_AGE")}}>Sort by age</button>
<button onClick={()=>{this.importPersonsIds("SORT_BY_NAME)}}>Sort by name</button>
<div>
{this.state.personsThumbnails}
</div>
</div>
);
}
}
class SinglePersonThumbnail extends Component {
constructor(props) {
super(props);
this.state = {
photoUrl : undefined,
personsName : undefined
}
}
componentDidMount() {
// Called when component is created
this.importDataAndPhotoForPerson(this.props.id);
}
componentWillReceiveProps(nextProps) {
// Called always when ComponentManager changes the order of thumbnails upon other sorting mode triggered
this.importDataAndPhotoForPerson(this.props.id);
}
importDataAndPhotoForPerson(id) {
// Imports name of the person and link to photo stored in state
}
render() {
return(
// Display image by link and person's name based on photoUrl and personsName states!
);
}
}
you can use componentDidUpdate() and can see the documentation from official site.
componentWillReceiveProps is outdated and not recommended. A better idea will be to store the data from backend in some context/redux. This will cause the child components to re-render with updated data.
For a given route localhost:3000/my-link, component MyLink is rendered.
In MyLink component, ajax request is fired in componentDidMount() which then sets the state object of the MyLink class and renders the component.
But when navigating to some other route and switching back to /my-link ajax is fired again. In short, whole state previously populated is lost. Can there be a way to check if the previous state is already populated, then prevent componentDidMount to be called?
componentDidMount() is invoked immediately everytime a component is mounted in the DOM. What you're trying to do can be achieved by integrating react redux store. Call the Ajax call according to store state variable. On firing the AJAX requests Dispatch the action to make the variable true.
componentDidMount() {
if(!this.props.variable)
{
//AJAX CALL
dispatch({ type: 'SET_VARIABLE', value: true });
}
}
Once it sets to true, it won't be called next time you navigate to this route.
When you switch from your existing route localhost:3000/my-link to any other page like localhost:3000/other-link, Your MyLink component got unmounted but at the same time, your data fetched in MyLink component is still in your redux store.
Here's what you can do;
If the data which you are fetching in MyLink is frequently changing then it is a good idea to fetch it again when you visit the same link again. Means, You need to clear the state when your component unmounts.
componentWillUnmount() {
// resetData is an action which will clear the data in your MyLink reducer.
this.resetData();
}
So, When you visit the same link again, The component won't have any previous data and it will fetch the fresh data.
If that data is not frequently changing and you're okay with getting it just one time then you can make a conditional call to your action.
componentDidMount() {
// this.props.data is a state from redux store.
if(!this.props.data){
this.getData();
}
}
I'm trying to get the authentication info into React component and fetch data related to the authenticated user, but componentDidMount is not getting the auth value from the redux store unless setTimeOut is used.
How Do I go about it?
I tried componentWillReceiveProps() , but that does not work either .
class Dashboard extends Component {
componentDidMount = () => {
console.log(this.props.auth);
setTimeout(
() => console.log(this.props.auth),
100
)
}
Only console.log within setTimeout returns the value
I had this same issue a while back and what helped me was to think about my component as a function.
What I would do is display a spinner, until auth is not null, then render a different view once it is ready.
class Dashboard extends Component {
render = () => {
if(this.props.auth === null){
return <SpinnerComponent />
}
return ....
}
}
What will happen is that:
Before props.auth is ready, the component will render a spinner
After props.auth is ready, the component will render your normal app
Rerendering happens when props are change (ie redux is changed)
I have a simple app which consists of a home component , an add component , Listing component. Home component has listing and add component. In add component i am adding some items to local storage and in listing component i am retrieving those items and showing as a list. Listing component doesn't have any props, it directly gets the data from local storage and returns ul list. I wanted to know, is there a way that when I save my data to local storage, at the same time my list should be updated that means there has to be some way by that if i click on save button the listing component should render with updated list. Can i trigger the render method of any component manually without using states.
this.forceUpdate() does work, but is in most cases a cheap fix which you want to avoid. You could have a the listings in two places: Local Storage for the actual saving, the state to trigger re-rendering when needed. Your app might look something like this:
constructor() {
super()
this.state = {
listings: []
}
}
componentWillMount() {
localStorage.getItem('listings')
this.setState({ listings })
}
saveListing(newListing) {
const listings = this.state.listings.concat([])
listings.push(newListing)
localStorage.setItem('listings', listings)
this.setState({ listings })
}
render() {
return(
<div>
<ul>
{
this.state.listings.map(listing => {
return(
<ListingComponent key={listing.id} listing={ ...listing } />
)
})
}
</ul>
<AddListingComponent addListing={(newListing) => this.saveListing(newListing)} />
</div>
)
}
There is a method called this.forceUpdate() to re-render , so just call this method after each element added to local storage
addItem(elm){
localStorage.yourArray.push(elm);
this.forceUpdate();
}
I am new to using React and React Router.
The home page (home component) loads data via an ajax request to the server which displays the data.
Then people can navigate within the app using react router. But when someone goes back to the home page (home component) the ajax call is again made to the server which is unnecessary.
So basically when you're navigating within components using react router each time a component is loaded ajax calls are made every time.
Is there something we can do to maybe cache the data like in Angular where we don't have to make the ajax calls again and again when a component is loaded.
Thanks
class NewComponent extends React.Component {
constructor(){
super();
this.state = {
data : []
}
}
componentWillMount() {
let length = Object.keys(this.state.data).length;
if(length === 0){
let req = new Request('https://example.com/json', {
mode: 'cors',
method: 'get'
});
this.serverRequest = fetch(req).then(function (result) {
return result.json()
}).then(function(j){
this.setState({
data: j.up
})
}.bind(this));
}
}
render(){
return(
some html
)
}
You'll probably want to store all your state in your topmost app component, which should only re-mount when the page is reloaded. That way the state can stay in there, and be passed down to child components.
If it's a larger project, you might consider using a state management system alongside React like redux.
Another idea is to move your ajax functions out into their own module, which gets imported into your component. Then you can place some caching in your ajax module, away from your component.