React router loading data each time - javascript

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.

Related

React reuses components instead of creating new so that I have to rely on componentWillReceiveProps to load fresh data

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.

How to NOT initiate ComponentDidMount() method in React 17

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?

How can componentDidMount wait for a value from redux store before executing (without using setTimeout)?

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)

React SSR - Rendering a component following an async action

React's component lifecycle goes hand in hand with the DOM, so when I'm trying to render on the server side following an async action I'm encountering issues.
I have a component (let's call it the containing component) which is responsible for dynamically importing another component (let's call it the inner component) according to data passed in its props.
Once the import promise is resolved, I'd like to render the inner component inside of the containing component.
The problem is, I have no way of accessing the containing component's lifecycle.
render() is only triggered once upon the construction of the containing component.
Updating the component via forceUpdate() and setState() are not possible for the same reason componentDidMount() will never be called.
How do I render a component following an async action on the server side when using server rendering?
My code so far:
import React from 'react';
export default class ComponentLoader extends React.Component<{ component: string }, { component: any }> {
constructor(props) {
super(props);
this.state = {
component: null
}; //no-op
}
componentDidMount(): void {
//never called
}
componentWillMount(): void {
import('./' + this.props.component).then(module => {
this.forceUpdate(); //no-op
this.setState({ component: React.createElement(module.default) }); //no-op
});
}
render() {
//called only once during construction
return this.state.component || null; //this.state won't be available, nor will it matter if I access component any other way since render() won't be called again once that component is resolved anyway.
}
}
I ended up using loadable components, a different implementation than what you see in the comments though.
https://github.com/jamiebuilds/react-loadable
https://github.com/konradmi/react-loadable-ssr-code-splitting
On the server side it makes sure to only render the react components once all the dependencies are loaded async. That way the render is sync since that's the only thing that's supported at the moment (though there's a feature request for supporting async react ssr), but the loading is done async, so it doesn't block any other operations.

React - Call setState in parent when child is called

I am building a blog in react where I get the data from a JSON-API and render it dynamically. I call setState in my app.js to get the data from JSON via axios and route with match.params to the post pages(childs). This works fine, BUT if I call the child (URL: ../blog/post1) in a seperate window it doesnt get rendered. It works if I hardcode the state.
So, I see the issue, what would be the best way to fix it?
Parent:
class App extends Component {
constructor(props) {
super();
this.state = {
posts: []
}
getPosts() {
axios.get('APIURL')
.then(res => {
let data = res.data.posts;
this.setState({
posts: data
})
})
}
componentDidMount = () => this.getPosts()
Child:
UPDATE - Found the error:
The component threw an error, because the props were empty. This was because the axios getData took some time.
Solution: I added a ternary operator to check if axios is done and displayed a css-spinner while loading. So no error in component.
Dynamic Routing works like a charme.
You can do this with binding. You would need to write a function like
setPosts(posts) {
this.setState({posts});
}
in the parent, and then in the parent, bind it in the constructor, like so.
this.setPosts = this.setPosts.bind(this);
When you do this, you're attaching setPosts to the scope of the parent, so that all mentions of this in the function refer to the parent's this instead of the child's this. Then all you need to do is pass the function down to the child in the render
<Child setPosts={this.setPosts} />
and access that method in the child with
this.props.setPosts( /*post array goes here */ );
This can apply to your getPosts method as well, binding it to the parent class and passing it down to the Child.
When you hit the /blog/post1 url, it renders only that page. The App component is not loaded. If you navigate from the App page, all the loading has been done and you have the post data in the state of the child component.
Please refer to this SO question. This should answer your question.

Categories

Resources