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;