Repository: freeCodeCamp/freeCodeCamp
File: client/src/templates/Introduction/components/super-block-accordion.tsx (TypeScript)

Code before:
    </Disclosure>
  );
};

const Module = ({ dashedName, children, isExpanded }: ModuleProps) => {
  const { t } = useTranslation();

  return (
    <Disclosure as='li' defaultOpen={isExpanded}>
      <Disclosure.Button className='module-button'>
        <DropDown />
        {t(`intro:full-stack-developer.modules.${dashedName}`)}
      </Disclosure.Button>
      <Disclosure.Panel as='ul' className='module-panel'>
        {children}
      </Disclosure.Panel>
    </Disclosure>
  );
};

export const SuperBlockAccordion = ({
  challenges,
  superBlock,
  chosenBlock
}: SuperBlockTreeViewProps) => {
  const content = useMemo(() => {
    const chapters = uniq(challenges.map(({ chapter }) => chapter as string));
    const examBlock = challenges.find(
      ({ blockType }) => blockType === BlockTypes.exam
    );

    // Expand the outer layers in order to reveal the chosen block.
    const expandedChapter = challenges.find(
      ({ block }) => chosenBlock === block
    )?.chapter as string;
    const expandedModule = challenges.find(({ block }) => chosenBlock === block)
      ?.module as string;

    return chapters.map(chapter => {
      if (examBlock && chapter === examBlock.chapter) {
        return (
          <li key={examBlock.dashedName} className='exam'>
            <Block
              block={examBlock.dashedName}
              blockType={examBlock.blockType}
              challenges={[examBlock]}
              superBlock={superBlock}
            />
          </li>
        );
      }

Review comment (suggestion):
`useMemo` can help here, but currently it's not doing a lot. While `challenges` never changes, `chosenBlock` does and each time it does we recalculate a few things we don't need to.

So, rather than putting the whole thing in a memo, we can memoize just the parts that depend on challenges.

I'll push a commit in a bit so you can see what I'm getting at.

Code after:
    </Disclosure>
  );
};

const Module = ({ dashedName, children, isExpanded }: ModuleProps) => {
  const { t } = useTranslation();

  return (
    <Disclosure as='li' defaultOpen={isExpanded}>
      <Disclosure.Button className='module-button'>
        <DropDown />
        {t(`intro:full-stack-developer.modules.${dashedName}`)}
      </Disclosure.Button>
      <Disclosure.Panel as='ul' className='module-panel'>
        {children}
      </Disclosure.Panel>
    </Disclosure>
  );
};

export const SuperBlockAccordion = ({
  challenges,
  superBlock,
  chosenBlock
}: SuperBlockTreeViewProps) => {
  const { allChapters, allBlocks, examChallenge } = useMemo(() => {
    const allBlocks = uniqBy(challenges, 'block').map(
      ({ block, blockType, chapter, module }) => ({
        name: block,
        blockType,
        chapter: chapter as string,
        module: module as string,
        challenges: challenges.filter(({ block: b }) => b === block)
      })
    );

    const allModules = uniqBy(allBlocks, 'module').map(
      ({ module, chapter }) => ({
        name: module,
        chapter,
        blocks: allBlocks.filter(({ module: m }) => m === module)
      })
    );

    const allChapters = uniqBy(allModules, 'chapter').map(({ chapter }) => ({
      name: chapter,
      modules: allModules.filter(({ chapter: c }) => c === chapter)
    }));

    const examChallenge = challenges.find(
      ({ blockType }) => blockType === BlockTypes.exam