Skip to content

Advanced React

1. Moving state down

export default function App() {
  // const [isOpen, setIsOpen] = useState(false);

  return (
    <div className="layout">
      <Header />

      {/* <button onClick={() => setIsOpen(true)}>Open Dialog</button>

      {isOpen ? <ModalDialog /> : null} */}

      <ButtonWithModalDialog />

      <BunchOfStuff />

      <OtherStuffAlsoComplicated />

      <Footer />
    </div>
  );
}

2. Components as props / Children as props

Before

export default function MainScrollableArea() {
  const [position, setPosition] = useState(300);

  const onScroll = (e) => {
    const calculated = getPosition(e.target.scrollTop);
    setPosition(calculated);
  };

  const getPosition = (val) => {
    return val;
  };

  return (
    <div className="scrollable-block" onScroll={onScroll}>
      <MovingBlock position={position} />
      <BunchOfStuff />
      <OtherStuffAlsoComplicated />
    </div>
  );
}

After

export default function ScrollableWithMovingBlock({ content }) {
  const [position, setPosition] = useState(0);
  const onScroll = () => {
    setPosition();
  }; // Same as before

  return (
    <div className="scrollable-block" onScroll={onScroll}>
      <MovingBlock position={position} />
      {content}
    </div>
  );
}


export default function App() {
  const slowComponents = (
    <>
      <BunchOfStuff />
      <OtherStuffAlsoComplicated />
      <Footer />
    </>
  );

  return <ScrollableWithMovingBlock content={slowComponents} />;
}

// Or can written as

export default function App() {
    return (
        <ScrollableWithMovingBlack>
            <BunchOfStuff />
            <OtherStuffAlsoComplicated />
            <Footer />
        </ScrollableWithMovingBlack>
    );
}

3. Configuration concerns with elements as props

Button Example

export default function Button({ icon }) {
  return <button>Submit {icon}</button>;
}

<Button icon={<Loading />} />;
<Button icon={<Warning color="yellow" />} />;

ModalExample

const ModalDialog = ({ content, footer }) => {
  return (
    <div className="modal-dialog">
      <div className="content">{content}</div>
      <div className="footer">{footer}</div>
    </div>
  );
};

<ModalDialog content={<SomeFormHere />} footer={<SubmitButton />}>

<ModalDialog content={<SomeFormHere />} footer={<><SubmitButton /><CancelButton /></>}>

Three Columns Layout

<ThreeColumnsLayout
  leftColumn={<Something />}
  middleColum={<OtherThing />}
  rightColumn={<SomethingElse />}
/>

Conditional Rendering and performance

const App = () => {
  const [isDialogOpen, setIsDialogOpen] = useState(false);

  //When is this one going to be rendered
  const footer = <Footer />;

  return isDialogOpen ? <ModalDialog footer={footer} /> : null;
};

const ModalDialog = ({ children, footer }) => {
  return (
    <div className="dialog">
      <div className="content">{children} </div>
      {/* Whatever is coming from footer prop is going to be
rendered only when this entire component renders */} {/* not sooner */}
      <div className="footer">{footer}</div>
    </div>
  );
};

Default values for the elements from props

// primary button should have white icons
<Button appearance="primary" icon={<Loading color="white" />} />

// secondary button should have black icons
<Button appearance="secondary" icon={<Loading color="black" />} />

// Large button should have large icons
<Button size="large" icon={<Loading size="large" />} />
export default function Button({ appearance, size, icon }) {
  //create default props
  const defaultIconProps = {
    size: size === "large" ? "large" : "medium",
    color: appearance === "primary" ? "white" : "black",
  };

  const newProps = {
    ...defaultIconProps,
    // make sure that props that are coming from the icon override default if they exist
    ...icon.props,
  };

  // clone the icon and assign new props to it
  const clonedIcon = React.cloneElement(icon, newProps);
  return <button>Submit {clonedIcon}</button>;
}


// primary button will have white icons
<Button appearance="primary" icon={<Loading />} />

// secondary button will have black icons
<Button appearance="secondary" icon={<Loading/>} />

// Large button will have large icons
<Button size="large" icon={<Loading />} />

// override the default black color with red icons
<Button
appearance="secondary" icon={<Loading color="red" />}
/>

4. Advanced confuguration with render props