Step 6: Make an endpoint request
You can now make a request to the /api/v5/recognitions
endpoint and display the results in the Modules
component using the fetch() API provided by all modern web browsers. The starter app provides a polyfill for fetch()
so you do not have to worry about support from older browsers as mentioned in the documentation for the starter App.
1. Retreive the list of modules
To properly display the recognition interface, you are going to retrieve the list of modules from an API response. While the list is being retrieved, a loading screen will be displayed, so you can work on that first. You will manage this screen with a new state variable named isLoadingModules
which you will add to Main.js
.
Add a constructor to Main.js
that sets the isLoadingModules
state:
src/Main.js
+ constructor(props) {
+ super(props);
+ this.state = {
+ isLoadingModules: true,
+ }
+ }
And then update the render()
method to examine this variable:
render() {
+ const { isLoadingModules } = this.state;
+
+ if (isLoadingModules) {
+ return (
+ <div className='anywhereRecognition'>
+ <Segment inverted className='loader-segment'>
+ <Dimmer inverted active>
+ <Loader content='Loading' />
+ </Dimmer>
+ </Segment>
+ </div>
+ )
+ }
+
return (
<div className='anywhereRecognition'>
Update Main.css
with the new style:
+.loader-segment {
+ height: 100px;
+}
Now your App should display a spinning loader:

2. Retrieve data from the API
Next, you will add the function that retrieves the data from the API and calls it from componentDidMount()
. The componentDidMount()
method will then be added to Main.js
.
First, add the following file to the project:
src/apiMethods.js
import buildUrl from 'build-url';
function fetchModules(accessToken) {
const url = buildUrl(process.env.REACT_APP_API_DOMAIN, {
path: '/api/v5/recognitions',
});
return fetch(url, {
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
credentials: 'same-origin',
method: 'OPTIONS',
}).then(res => {
if (res.status >= 200 && res.status < 300) {
return res;
}
const err = new Error(res.statusText);
err.res = res;
throw err;
}).then(res => res.json());
}
export {
fetchModules,
};
In Main.js
add the following import
statement and componentDidMount()
method:
src/Main.js
+import { fetchModules } from './apiMethods';
+componentDidMount() {
+ const { accessToken } = this.props;
+ fetchModules(accessToken)
+ .then(res => {
+ this.setState({ modules: res.items, isLoadingModules: false });
+ })
+ .catch(err => {
+ this.setState({ error: err, isLoadingModules: false });
+ });
+}
Notice that this method sets isLoadingModules: false
when the modules have been retrieved. Now you need to pass the modules into the Modules
component by making the following changes to the render()
method of Main.js
.
render() {
- const { isLoadingModules } = this.state;
+ const { isLoadingModules, modules } = this.state;
...snip...
return (
<div className='anywhereRecognition'>
<Form>
<Recipients />
<Reason />
- <Modules />
+ <Modules modules={modules} />
<Button color='green' floated='right'>
3. Update the Module
component
Module
componentNext, you'll update the Module
component to use the new data in modules
prop and introduce an Accordion component from the Semantic UI React library for displaying the modules. You will also set up two functions - one for looping over and rendering all of the modules, and another that loops over and renders all of the recognition values for each module. Make the following changes to Modules.js
:
src/Modules.js
Update the imports as follows:
-import { Header } from 'semantic-ui-react';
+import { Accordion, Icon, Header, Image } from 'semantic-ui-react';
Update the render
method as follows:
render() {
+ const { modules } = this.props;
+ const moduleItems = modules.map((module, i) => this.RenderModule(module, i));
return (
<div className="modules">
<Header as="h2" content="Choose a recognition type" />
+ <Accordion fluid>
+ {moduleItems}
+ </Accordion>
</div>
)
Add the RenderModule()
and RenderCriteria()
methods:
RenderModule(module, i) {
const { activeModuleIndex } = this.state;
const criteriaItems = module.criteria.map((criterion, i) => this.RenderCriteria(criterion, i));
return (
<div key={module.id}>
<Accordion.Title active={activeModuleIndex === i} index={i} onClick={this.handleModuleClick} className="module-wrapper">
<div className="module-image">
<Image src={module.bannerURL} size='small' />
</div>
<div className="module-info">
<Header>
{module.name} <Icon name='dropdown' />
</Header>
<div dangerouslySetInnerHTML={{__html: module.description}} />
</div>
</Accordion.Title>
<Accordion.Content active={activeModuleIndex === i}>
<Accordion fluid>
{criteriaItems}
</Accordion>
</Accordion.Content>
</div>
)
}
RenderCriteria(criterion, j) {
const { activeCriterionIndex } = this.state;
return (
<div key={criterion.id}>
<Accordion.Title active={activeCriterionIndex === j} index={j} criteriaid={criterion.id} onClick={this.handleCriterionClick} content={criterion.name} />
<Accordion.Content active={activeCriterionIndex === j}>
<div className="criterion-wrapper">
<div className="criterion-image">
<Image src={criterion.criterionBannerUrl} size="small" />
</div>
<div className="criterion-info" dangerouslySetInnerHTML={{__html: criterion.description}} />
</div>
</Accordion.Content>
</div>
)
}
Along with code for handling the clicks:
handleCriterionClick = (e, titleProps) => {
const { index, criteriaid } = titleProps
const { activeCriterionIndex } = this.state
const newIndex = activeCriterionIndex === index ? -1 : index
this.setState({ activeCriterionIndex: newIndex })
}
handleModuleClick = (e, titleProps) => {
const { index } = titleProps
const { activeModuleIndex } = this.state
const newIndex = activeModuleIndex === index ? -1 : index
this.setState({ activeModuleIndex: newIndex, activeCriterionIndex: -1 })
}
Add a constructor to set appropriate defaults:
constructor(props) {
super(props);
this.state = {
activeModuleIndex: -1,
activeCriterionIndex: -1,
}
}
Add the following to the Modules.css
file:
src/Modules.css
.module-wrapper {
padding: 10px;
display: flex;
}
.criterion-wrapper {
padding: 10px;
display: flex !important;
}
.module-image {
flex-grow: 0;
flex-shrink: 0;
flex-basis: 20%;
margin-left: 0px;
}
.criterion-image {
flex-grow: 0;
flex-shrink: 0;
flex-basis: 20%;
margin-left: 20px;
}
If you look at your app in a browser, it might look something like this now:

You can click on the modules to expand them and display the list of recognition values. You may see an "Email Recognition" module that isn't normally usable but which, at the time of writing, is returned by the OPTIONS
request. Dealing with points-based recognition modules can be complex and is beyond the scope of this tutorial. To exclude points-based and Email modules, you will make a change to the fetchModules()
method in apiMethods.js
.
- throw err;
- }).then(res => res.json());
+ }).then(res => res.json())
+ .then(res => {
+ res.items = res.items.filter(item => item.moduleType === 'FREE' && !item.name.includes('Email'));
+ return res;
+ });
}
With the modules and recognition values now rendering, you can focus on updating the Recipients
component so it can fetch users from the Achievers API.
Updated over 5 years ago