ReactJS server side async for search engines - reactjs

I'm rendering my website server side with ReactJS + Router
and many of my components make a REST call to generate content. This content won't be send as HTML server side since it's an async call.
A component which could look like this:
import React from 'react'
import ReactDOM from 'react-dom'
// Imports omitted
export default class MyPage extends React.Component {
constructor() {
super();
this.state = {items: []};
fetch("http://mywebservice.com/items").then((response) => {
response.json().then((json) => {
this.setState({items: json.items})
}).catch((error) => {});
});
}
render() {
if (this.state.items && this.state.items.length > 0) {
var rows = [];
// Go through the items and add the element
this.state.items.forEach((item, i) => {
rows.push(<div key={item.id}></div>);
});
return <div>
<table>
{rows}
</table>
</div>;
}
else {
return <span>Loading...</span>
}
}
}
a search engine would index "Loading..." while I obviously want my elements (items) indexed. Is there a way to solve this?

Checkout react-async (https://www.npmjs.com/package/react-async) or react-async-render (https://www.npmjs.com/package/react-async-render)
Sounds like it's just what you need.

In theory, I think in order to accomplish that you will need on server side request handler, collect all necessary data and pass it to ReactDOM.RenderToString() as initial state JSON string, then create custom fetch() which will check if the initial data exists, if yes then it will render html right away.

One of the reasons why redux is so popular is that it enforces data separation. Ideally, you have a single place where you specify all the data application needs to render. In redux that'll be a store, but let's assume you store all the data in the topmost element's props. If that is the case, all you need to do is to provide all the relevant props to that topmost element and it will render everything nicely. The way to collect all the data needed is up to server though. I would suggest getting clues for that from current url and logged-in user info (be it a token or a cookie).

Related

How read array/object from get Axios in React function [duplicate]

I have recently moved from Angular to ReactJs. I am using jQuery for API calls. I have an API which returns a random user list that is to be printed in a list.
I am not sure how to write my API calls. What is best practice for this?
I tried the following but I am not getting any output. I am open to implementing alternative API libraries if necessary.
Below is my code:
import React from 'react';
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {
person: []
};
}
UserList(){
return $.getJSON('https://randomuser.me/api/')
.then(function(data) {
return data.results;
});
}
render() {
this.UserList().then(function(res){
this.state = {person: res};
});
return (
<div id="layout-content" className="layout-content-wrapper">
<div className="panel-list">
{this.state.person.map((item, i) =>{
return(
<h1>{item.name.first}</h1>
<span>{item.cell}, {item.email}</span>
)
})}
<div>
</div>
)
}
}
In this case, you can do ajax call inside componentDidMount, and then update state
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {person: []};
}
componentDidMount() {
this.UserList();
}
UserList() {
$.getJSON('https://randomuser.me/api/')
.then(({ results }) => this.setState({ person: results }));
}
render() {
const persons = this.state.person.map((item, i) => (
<div>
<h1>{ item.name.first }</h1>
<span>{ item.cell }, { item.email }</span>
</div>
));
return (
<div id="layout-content" className="layout-content-wrapper">
<div className="panel-list">{ persons }</div>
</div>
);
}
}
You may want to check out the Flux Architecture. I also recommend checking out React-Redux Implementation. Put your api calls in your actions. It is much more cleaner than putting it all in the component.
Actions are sort of helper methods that you can call to change your application state or do api calls.
Use fetch method inside componentDidMount to update state:
componentDidMount(){
fetch('https://randomuser.me/api/')
.then(({ results }) => this.setState({ person: results }));
}
This discussion has been for a while and #Alexander T.'s answer provided a good guide to follow for newer of React like me. And I'm going to share some additional know-how about calling the same API multiple times to refresh the component, I think it's probably a common question for beginners.
componentWillReceiveProps(nextProps), from official documentation :
If you need to update the state in response to prop changes (for
example, to reset it), you may compare this.props and nextProps and
perform state transitions using this.setState() in this method.
We could conclude that here is the place we handle props from the parent component, have API calls, and update the state.
Base on #Alexander T.'s example:
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {person: []};
}
componentDidMount() {
//For our first load.
this.UserList(this.props.group); //maybe something like "groupOne"
}
componentWillReceiveProps(nextProps) {
// Assuming parameter comes from url.
// let group = window.location.toString().split("/")[*indexParameterLocated*];
// this.UserList(group);
// Assuming parameter comes from props that from parent component.
let group = nextProps.group; // Maybe something like "groupTwo"
this.UserList(group);
}
UserList(group) {
$.getJSON('https://randomuser.me/api/' + group)
.then(({ results }) => this.setState({ person: results }));
}
render() {
return (...)
}
}
Update
componentWillReceiveProps() will be deprecated.
Here are only some methods (all of them in Doc) in the life cycle I think that they are related to deploying API in the general cases:
By referring to the diagram above:
Deploy API in componentDidMount()
The proper scenario to have API call here is that the content (from the response of API) of this component will be static, componentDidMount() only fire once while the component is mounting, even new props are passed from the parent component or have actions to lead re-rendering.
The component do check difference to re-render but not re-mount.
Quote from doc:
If you need to load data from a remote endpoint, this is a good place to
instantiate the network request.
Deploy API in static getDerivedStateFromProps(nextProps, prevState)
We should notice that there are two kinds of component updating, setState() in current component would not trigger this method but re-rendering or new props from parent component would.
We could find out this method also fires while mounting.
This is a proper place to deploy API if we want to use the current component as a template, and the new parameters to make API calls are props coming from parent component.
We receive a different response from API and return a new state here to change the content of this component.
For example:
We have a dropdown list for different Cars in the parent component, this component needs to show the details of the selected one.
Deploy API in componentDidUpdate(prevProps, prevState)
Different from static getDerivedStateFromProps(), this method is invoked immediately after every rendering except the initial rendering. We could have API calling and render difference in one component.
Extend the previous example:
The component to show Car's details may contain a list of series of this car, if we want to check the 2013 production one, we may click or select or ... the list item to lead a first setState() to reflect this behavior (such as highlighting the list item) in this component, and in the following componentDidUpdate() we send our request with new parameters (state). After getting the response, we setState() again for rendering the different content of the Car details. To prevent the following componentDidUpdate() from causing the infinity loop, we need to compare the state by utilizing prevState at the beginning of this method to decide if we send the API and render the new content.
This method really could be utilized just like static getDerivedStateFromProps() with props, but need to handle the changes of props by utilizing prevProps. And we need to cooperate with componentDidMount() to handle the initial API call.
Quote from doc:
... This is also a good place to do network requests as long as you
compare the current props to previous props ...
I would like you to have a look at redux
http://redux.js.org/index.html
They have very well defined way of handling async calls ie API calls, and instead of using jQuery for API calls, I would like to recommend using fetch or request npm packages, fetch is currently supported by modern browsers, but a shim is also available for server side.
There is also this another amazing package superagent, which has alot many options when making an API request and its very easy to use.
You can also fetch data with hooks in your function components
full example with api call: https://codesandbox.io/s/jvvkoo8pq3
second example: https://jsfiddle.net/bradcypert/jhrt40yv/6/
const Repos = ({user}) => {
const [repos, setRepos] = React.useState([]);
React.useEffect(() => {
const fetchData = async () => {
const response = await axios.get(`https://api.github.com/users/${user}/repos`);
setRepos(response.data);
}
fetchData();
}, []);
return (
<div>
{repos.map(repo =>
<div key={repo.id}>{repo.name}</div>
)}
</div>
);
}
ReactDOM.render(<Repos user="bradcypert" />, document.querySelector("#app"))
1) You can use Fetch API to fetch data from Endd Points:
Example fetching all Github repose for a user
/* Fetch GitHub Repos */
fetchData = () => {
//show progress bar
this.setState({ isLoading: true });
//fetch repos
fetch(`https://api.github.com/users/hiteshsahu/repos`)
.then(response => response.json())
.then(data => {
if (Array.isArray(data)) {
console.log(JSON.stringify(data));
this.setState({ repos: data ,
isLoading: false});
} else {
this.setState({ repos: [],
isLoading: false
});
}
});
};
2) Other Alternative is Axios
Using axios you can cut out the middle step of passing the results of
the http request to the .json() method. Axios just returns the data
object you would expect.
import axios from "axios";
/* Fetch GitHub Repos */
fetchDataWithAxios = () => {
//show progress bar
this.setState({ isLoading: true });
// fetch repos with axios
axios
.get(`https://api.github.com/users/hiteshsahu/repos`)
.then(result => {
console.log(result);
this.setState({
repos: result.data,
isLoading: false
});
})
.catch(error =>
this.setState({
error,
isLoading: false
})
);
}
Now you can choose to fetch data using any of this strategies in componentDidMount
class App extends React.Component {
state = {
repos: [],
isLoading: false
};
componentDidMount() {
this.fetchData ();
}
Meanwhile you can show progress bar while data is loading
{this.state.isLoading && <LinearProgress />}
Render function should be pure, it's mean that it only uses state and props to render, never try to modify the state in render, this usually causes ugly bugs and decreases performance significantly. It's also a good point if you separate data-fetching and render concerns in your React App. I recommend you read this article which explains this idea very well. https://medium.com/#learnreact/container-components-c0e67432e005#.sfydn87nm
This part from React v16 documentation will answer your question, read on about componentDidMount():
componentDidMount()
componentDidMount() is invoked immediately after a component is
mounted. Initialization that requires DOM nodes should go here. If you
need to load data from a remote endpoint, this is a good place to
instantiate the network request. This method is a good place to set up
any subscriptions. If you do that, don’t forget to unsubscribe in
componentWillUnmount().
As you see, componentDidMount is considered the best place and cycle to do the api call, also access the node, means by this time it's safe to do the call, update the view or whatever you could do when document is ready, if you are using jQuery, it should somehow remind you document.ready() function, where you could make sure everything is ready for whatever you want to do in your code...
As an addition/update to Oleksandr T.'s excellent answer:
If you use class components, backend calls should happen in componentDidMount.
If you use hooks instead, you should use the effect hook
For example:
import React, { useState, useEffect } from 'react';
useEffect(() => {
fetchDataFromBackend();
}, []);
// define fetchDataFromBackend() as usual, using Fetch API or similar;
// the result will typically be stored as component state
Further reading:
Using the Effect Hook in the official docs.
How to fetch data with React Hooks? by Robin Wieruch
A clean way is to make an asynchronous API call inside componentDidMount with try/catch function.
When we called an API, we receive a response. Then we apply JSON method on it, to convert the response into a JavaScript object. Then we take from that response object only his child object named "results" (data.results).
In the beginning we defined "userList" in state as an empty array. As soon as we make the API call and receive data from that API, we assign the "results" to userList using setState method.
Inside the render function we tell that userList will be coming from state. Since the userList is an array of objects we map through it, to display a picture, a name and a phone number of each object "user". To retrieve this information we use dot notation (e.g. user.phone).
NOTE: depending on your API, your response may look different. Console.log the whole "response" to see which variables you need from it, and then assign them in setState.
UserList.js
import React, { Component } from "react";
export default class UserList extends Component {
state = {
userList: [], // list is empty in the beginning
error: false
};
componentDidMount() {
this.getUserList(); // function call
}
getUserList = async () => {
try { //try to get data
const response = await fetch("https://randomuser.me/api/");
if (response.ok) { // ckeck if status code is 200
const data = await response.json();
this.setState({ userList: data.results});
} else { this.setState({ error: true }) }
} catch (e) { //code will jump here if there is a network problem
this.setState({ error: true });
}
};
render() {
const { userList, error } = this.state
return (
<div>
{userList.length > 0 && userList.map(user => (
<div key={user}>
<img src={user.picture.medium} alt="user"/>
<div>
<div>{user.name.first}{user.name.last}</div>
<div>{user.phone}</div>
<div>{user.email}</div>
</div>
</div>
))}
{error && <div>Sorry, can not display the data</div>}
</div>
)
}}
As best place and practice for external API calls is React Lifecycle method componentDidMount(), where after the execution of the API call you should update the local state to be triggered new render() method call, then the changes in the updated local state will be applied on the component view.
As other option for initial external data source call in React is pointed the constructor() method of the class. The constructor is the first method executed on initialization of the component object instance. You could see this approach in the documentation examples for Higher-Order Components.
The method componentWillMount() and UNSAFE_componentWillMount() should not be used for external API calls, because they are intended to be deprecated. Here you could see common reasons, why this method will be deprecated.
Anyway you must never use render() method or method directly called from render() as a point for external API call. If you do this your application will be blocked.
You must try "axios" library for API call.
Instead of direct using jQuery.
Thanks.
It would be great to use axios for the api request which supports cancellation, interceptors etc. Along with axios, l use react-redux for state management and redux-saga/redux-thunk for the side effects.

Create a single instance of a React UI component and passing down to children

I'm using React and my application has a side panel that displays information from various parts of the app. I tried updating the panel information through state and props but the problem is that the UI rerenders unrelated parts of the app because I need to put the state high up in the tree. I want this panel position to be fixed, and the data to only update from the relevant child components. I thought a good solution is to create the panel high up in the tree hierarchy and then pass its reference to children so that they can update it without rerendering the whole app. I have 2 questions:
Is creating the side panel high up and passing a single reference to children the right approach?
If yes, how can I do this?
I finally figured out the answer which was inspired by this example: https://codesandbox.io/s/3y18l37wm6.
I'm still hoping to find a better solution, having a Singleton doesn't sit well with me, and I don't know if it's a good approach in general.
Here are the steps...
First, create an index.js file for a module that represents the SidePane
const warn = () => console.log("haven't resgiter the SidePane yet");
let sidePane = {
open: warn,
};
/**
* this method is to save the reference of the singleton component
*/
export const registerSidePane = ref => {
if (ref) {
console.log("sidepane registered");
sidePane.open = ref.open;
} else {
console.log("sidepane unregistered");
sidePane.open = warn;
}
};
/**
* export the side pane component
*/
export { default as SidePaneInstance } from "./SidePane";
/**
* export the open function
*/
export default sidePane;
Second, create the component (call it SidePane.js) to which you want to pass your data, and structure it however you like.
export default class SidePane extends React.Component {
state = { component: <></> };
open = component => {
this.setState({ component: component });
}
render() {
return this.state?.component
}
}
Third, place this pane wherever you want in the app and register the ref like this:
<SidePaneInstance ref={registerSidePane} />
Finally, pass the data dynamically from any component as follow:
SidePane.open(<div>it finally works!</div>)

Where to Put Code that should run First ReactJs + Mobx State Tree

I have some code that grabs the users ipAddres. I do this right now in my componentDidMount in my app.js
async componentDidMount() {
await eventTrackingStore.getIpAddress();
}
So I did it in my app.js as it is my root component and I only want to set this once. This works fine if the user starts from the home page and navigates through the site.
However some pages can be loaded directly(ie you type in the url in your browser and it goes straight to that page).
Since the react lifecycle starts with most immediate component, which calls a method that expects the ipAddress code to be set but it does not get set till it hits the app.js
Now I could put the above code in each method but that gets tedious. Is there some sort of method in reactjs, or mbox or mbox state tree that would fire first?
If you use mobx-state-tree and you have a global store, then that global store can make the API call in the afterCreate method
const AppModel = types.model({
ips: types.array(types.string)
}).actions(self => ({
afterCreate() {
flow(function*() {
const ips = yield eventTrackingStore.getIpAddress();
self.setIps(ips)
})()
},
setIps(ips: string[]) {
self.ips = ips
}
}))
OR
The same thing you can do in a wrapped react component that wrappes every page of your app.
class App extends React.Component {
componentDidMount() {
eventTrackingStore.getIpAddress().then(res => {
// set the ips into a store or any logic you want in order to pass them down to children
})
}
render() {
return this.props.children
}
}
I can think of two solutions:
You can use react context
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Use context to share the store between all components and if the data is not loaded, initialize loading right there in that nested component.
If the data is already there then just take it,
getIpAddress method should return a promise, so in case when data is already there it will be immediately resolved.

React — Passing data between siblings and iterating it

I have two questions, the first is about passing data between components and the second is about the component hierarchy.
Right now, in the Data component I am trying to set the name property and pass it to a ListItem component that should iterate based on the API request. I tried many things without success. What am I doing wrong? Does the data needs to be iterated when setting the new state? Am I passing it correctly?
Basic pseudocode
Read data from API
Set data to the component state
Create siblings components based on data stored
Render components
The second question is about the hierarchy of the components. I keep reading around the web that the data request should be setup at the top and separated. Having this in place, the other components would feed from this data and execute. Finally, the App component would render all this components accordingly. From the example below, am I going to the right track? Was I correct to creating a component specific for data request or should this be done in the App component?
I understand these questions might be basic but it is something that I am really struggling to understand and I would appreciate if someone can help me understand or point me to a basic example that I can digest.
Thank you in advance. (Sorry, I had more than two questions.)
class App extends React.Component {
render() {
return (
<ul>
<ListItem name={this.state.name} />
</ul>
)
}
}
class Data extends React.Component {
constructor(props) {
super(props)
this.state = {
name: '',
requestFailed: false,
}
}
componentDidMount() { // Executes after mouting
fetch('https://api.tfl.gov.uk/BikePoint')
.then((response) => {
return response.json()
}).then((d) => {
console.log('parsed json', d[0].commonName)
this.setState({
name: d.commonName
});
}).catch(function(ex) {
console.log('parsing failed', ex)
this.setState({
requestFailed: true
})
})
}
render() {
if(this.state.requestFailed) return <p>Request failed.</p>
if(!this.state.name) return <p>Loading</p>
const namesList = names.map(function(name, index){
return <ListItem key={index} name={this.state.name} />
})
return <ul>{ namesList }</ul>
}
}
class ListItem extends React.Component {
render() {
return <li> { this.props.name } </li>
}
}
ReactDOM.render(<App />, document.getElementById('app'));
CodePen
Where to start -
First, your App component needs to render Data component. React works in the way that parent element always renders children elements and what is not rendered doesn't exist.
Then, you need to remap response to names, if that is what you wanted to do - I am not sure.
In render method, you want to take name out of mapping function, not from state. I also removed name state, you really want to keep names instead of one name. There is a lot of small thing I had to adjust to make it work, so I will just post working code pen here, so you can see what needed to be done.
https://codepen.io/anon/pen/eEmqxX?editors=0010

Force React container to refresh data

In my React application, I have two distinct types of components: presentations and containers. It's roughly after Dan Abromov's "Presentational and Container Components", except I don't use Flux or Redux.
Now, I have the following structure:
UsersContainer
├── UsersListContainer
| └── UsersListView
└── AddUserContainer
The UsersListContainer is responsible for loading data from some REST API, and if that succeeds, it delegates presenting that data to the UsersListView.
The AddUserContainer is responsible for adding a new user, again, by invoking an REST API. Now, when that was successful, I would like the UsersListContainer to refresh its data.
The best I can think of is this:
class AddUserContainer extends React.Component {
render() {
// Other UI elements omitted for brevity
return (<button onClick={ e => props.onUserAdded() }>Add user</button>);
}
}
class UsersListContainer extends React.Component {
componentWillMount() {
// start fetching data using window.fetch;
// the promise callback will but retrieved data into this.state
}
render() {
return (<table></table>);
}
}
class UsersContainer extends React.Component {
render() {
const forceUpdate = this.setState({ ...this.state, refreshToken: Math.random() });
// Other UI elements omitted for brevity
<UsersListContainer key={ this.state.refreshToken } />
<AddUserContainer onUserAdded={ forceUpdate } />
}
}
But this approach feels like mis-using the key prop. Is there a better / more elegant way to do this?
Check out react-refetch, which provides a nice API for fetching, and allows you to implement the Presentational and Container Components pattern, without using Flux/Redux for API calls.
It also lets you handle loading and errored states, which is definitely necessary for a decent web application today.
In the example below, I got rid of UsersListContainer but moved AddUserContainer into UsersContainer as well. This makes your UsersListView the presentational component for UsersContainer. Feel free to change the naming as you wish. This is so that I can get the refreshUsers prop to pass into AddUserContainer.
// UsersContainer.js
const Container = ({ usersFetch, refreshUsers }) => {
if (userFetch.pending) {
return <LoadingDisplay />
} else if (usersFetch.rejected) {
return <ErrorDisplay error={ usersFetch.reason } />
} else if (usersFetch.fulfilled) {
return (
<UsersListView users={ usersFetch.value } />
<AddUserContainer handleAddUser={ refreshUsers } />
);
}
};
const refetch = (props) => {
const usersFetch = `/api/users`;
return {
usersFetch: usersFetch,
refreshUsers: () => ({
usersFetch: { ...usersFetch, force: true, refreshing: true }
}),
};
};
export default connect(refetch)(Container);
Check out the documentation for more examples. I personally prefer to use react-refetch for API-heavy applications, rather than implementing the calls in Redux.
If you don't want to use Flux or Redux yet, then updating peer-components (in your case, UsersListContainer and AddUserContainer) is a bit, in my opinion, anti-pattern for React.
The main idea of React is, to me, passing props from parent to children, therefore, Irvin Lim's idea to "got rid of UsersListContainer but moved AddUserContainer into UsersContainer" will make it easier for you to control when to update your component!
Your current approaching and my idea are the same: in your UsersContainer, create a method to forceUpdate it, then pass it along to AddUserContainer, and after this AddUserContainer added a user, you trigger that updating method on the parent:
<AddUserContainer onUserAdded={ this.props.updatingParent } />
For your reference, or for anyone else who wants to understand about how to update the parent component whenever child (or grandchild or great-grandchild) updates, please refer to my answer to another similar issue:
Re-initializing class on redirect
If you still keep your current component-hierarchy, when UsersContainer is updated, its child-components (UsersListContainer and AddUserContainer) will be updated, too. However, AddUserContainer will once again be updated!
As a result, I still think in your case, using Flux or Redux is a nice approaching, which eliminates to complexity of passing props through many levels of deep & complicated component-hierarchy

Resources