Tuesday, August 18, 2020

Avoiding name clashes when using HoC in React

After watching Michael Jackson's video on HoC vs render props, I noticed that the problems in HoC he mentioned can be avoided, albeit the component authors have to make their component's property names be dynamic. The component's property name(s) have to be explicitly set.

To cut to the chase, here's one implementation: https://codesandbox.io/s/compassionate-curie-i1x7w

App.js
import React from "react";
import "./styles.css";
import { withMouse } from "./Mouse";
import { withCat } from "./Cat";

const withMouseProp = "mouse";
const withCatProp = "spawnDate";

class App extends React.Component {
  render() {
    const mouseProp = this.props[withMouseProp];
    const catProp = this.props[withCatProp];
    const { message } = this.props;

    return (
      <div className="App">
        <h1>{message}</h1>
        <h2>Start editing to see some magic happen!</h2>
        mouse is at {mouseProp.x} {mouseProp.y}
        <br />
        Cat spawn date: {catProp}
      </div>
    );
  }
}

export default withMouse(
  withCat(App, withCatProp, withMouseProp),
  withMouseProp
);
Mouse.js
import React from "react";

export function withMouse(Component, mousePropName) {
  return class extends React.Component {
    state = { x: 0, y: 0 };

    handleMouseMove = (event) =  {
      this.setState({
        x: event.clientX,
        y: event.clientY
      });
    };

    render() {
      const withPropName = { ...this.props, [mousePropName]: this.state };
      return (
        <div onMouseMove={this.handleMouseMove}>
          <Component {...withPropName} />
        </div>
      );
    }
  };
}
Cat.js
import React from "react";

export function withCat(Component, catPropName, mousePropName) {
  return class extends React.Component {
    state = { x: 0, y: 0 };

    componentDidUpdate(prevProps) {
      const mouse = this.props[mousePropName];
      if (
        mouse.x !== prevProps[mousePropName].x ||
        mouse.y !== prevProps[mousePropName].y
      ) {
        this.setState(mouse);
      }
    }

    render() {
      const mouse = this.props[mousePropName];
      const withPropName = {
        ...this.props,
        [catPropName]: new Date().toISOString()
      };
      return (
        <>
          <Component {...withPropName} />
          <div>
            style={{
              position: "absolute",
              left: mouse.x,
              top: mouse.y,
              width: "25px",
              height: "25px",
              backgroundColor: "red"
            }}
          >
            cat
          </div>
        </>
      );
    }
  };
}

The above solution is moot point now, use React hooks instead.

No comments:

Post a Comment