Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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

...

  • 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`

Code Block
languagejsx
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;