Day 1, Session 2
Walkthrough and React Fundamentals:
Initialize app
In src/index.jsx
comment out lines 17-34, and replace with:ReactDOM.render(<div>Hello World</div>, document.getElementById('root'));
Fetch data from a variety of devstack apis
Display data in generic JSX
Generate custom display of devstack API data with Paragon components
Send messages to devstack API and update on response
Topics to review:
How does a react app “initialize”? What is different about MFE initialization? Why?
Brief understanding of tools:
Webpack
Babel
Eslint
Jest
Helpful simple ES6 syntax
React components
What makes a component?
How to define PropTypes?
What makes a component update (component lifecycle)?
Behavior vs render
Basic hooks for render (useState, useEffect)
Generic custom hook extraction
How to test generic react behavior
Network interaction
What are Promises? How do they work? How do we work with them?
How do we make asynchronous network requests?
Where should network requests be defined in the project?
How do we load data in from network api sources?
Example Output
Note: install react unit test utils with `npm install @edx/react-unit-test-utils`
import React from 'react';
import { useKeyedState } from '@edx/react-unit-test-utils';
import { Container } from '@edx/paragon';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { getConfig } from '@edx/frontend-platform/config';
const apis = {
courses: 'courses/v1/courses/',
mfe_context: 'mfe_context',
};
const { courses } = apis;
const courseAPIs = {
// blocks: (course_id) => `courses/v2/blocks/?course_id=${course_id}`,
instructorReports: (course_id) => `instructor/v1/reports/${course_id}`,
instructorTaskScheduledBulkEmails: (course_id) =>
`instructor_task/v1/schedules/${course_id}/bulk_email/`,
};
const useExamplePageData = () => {
const get = (uri) => getAuthenticatedHttpClient()
.get(`${getConfig().LMS_BASE_URL}/api/${uri}`);
const [apiObjects, setAPIObjects] = useKeyedState('apiObjects', {});
const [courseAPIObjects, setCourseAPIObjects] = useKeyedState('courseAPIObjects', {});
const logAPI = (key, uri) => {
get(uri).then(({ data }) => {
apiObjects[key] = data;
});
};
React.useEffect(() => {
Object.keys(apis).forEach(key => {
get(apis[key]).then(({ data }) => {
setAPIObjects({ ...apiObjects, [key]: data });
setAPIObjects((old) => ({ ...old, [key]: data }));
});
});
get('courses/v1/courses/').then(({ data }) => {
const { course_id } = data.results[0];
Object.keys(courseAPIs).forEach(key => {
get(courseAPIs[key](course_id)).then(({ data }) => {
setCourseAPIObjects((old) => ({ ...old, [key]: data }));
});
});
});
}, []); // empty list means only on first load.
/*
// called EVERY render
React.useEffect(() => { doThingAllTheTimes(); });
// called when myVal changes.
React.useEffect(() => { doThingWhenMyValChanges(myVal); }, [myVal]);
// called when EITHER myVal or val2 change.
React.useEffect(() => { doThingWhenMyValsChanges(myVal, val2); }, [myVal, val2]);
*/
// ^ Runs risk of inifinite loop, so safeguards generally added.
return { apiObjects, courseAPIObjects };
};
const ExamplePage = () => {
const { apiObjects, courseAPIObjects } = useExamplePageData();
return (
<main>
<Container className="py-5">
<h1>Example Page</h1>
<p>Hello world!</p>
<br />
<b>API Objects</b>
{Object.keys(apiObjects).map((key) => (
<pre>{JSON.stringify(apiObjects[key], null, 2)}</pre>
))}
<br />
<b>Course API Objects</b>
{Object.keys(apiObjects).map((key) => (
<pre>{JSON.stringify(apiObjects[key], null, 2)}</pre>
))}
</Container>
</main>
);
};
export default ExamplePage;