React - HOCs Inside Render - Y a-t-il des exceptions à la règle?

Oct 14 2020

J'ai implémenté un modèle Pub / Sub en utilisant plusieurs techniques de composition de composants: React.cloneElement et des «composants composés» fonctionnels. L'objectif en tête était de pouvoir inscrire dynamiquement des composants dans différents flux de données en leur attribuant un accessoire «sujet».

Par exemple, ce composant reçoit toutes les données publiées dans la rubrique HELLO_WORLD:

 <MyComponent topic="HELLO_WORLD" />

Voici l'intérieur de MyComponent exprimé en tant que composant fonctionnel:

export const MyComponent = props => subscribe(({ topic, data }) => {
    return <span>I am listening to the {topic} topic. Current state: {data}</span>
}, props.topic);

Alternativement, ici, il est exprimé comme un composant de classe:

class MyComponent extends React.Component {
    render() {
        const { props: { otherProps, topic } } = this;
        return subscribe(({ data }) => {
            return <span>I am listening to the {topic} topic. Current state: {data}</span>
        }, topic)
    }
}

Comme vous pouvez le voir, ce modèle nécessitait de renvoyer un composant d'ordre supérieur dans la fonction de rendu. Pensez-vous que cela relève de la mise en garde mentionnée ici ?

Voici un peu plus de contexte:

La fonction subscribe renvoie un composant composé:

const subscribe = (Comp, topic) => {
  return (
    <Subscriber topic={topic}>
      <Comp />
    </Subscriber>
  );
};

Qui encapsule MyComponent dans un abonné:

class Subscriber extends Component {
    state = publisher.getState(this.props.topic) // get initial state

    onMessage = msg => {
        this.setState({ ...msg });
        return this.state;
    }

    componentDidMount() {
        this.subscription = publisher
            .subscribe(this.props.topic, this.onMessage);
    }
    
    componentWillUnmount() {
        publisher.unsubscribe(this.props.topic, this.onMessage);
    }

    render() {
        const {
            state: { data },
            props: { children }
        } = this;
        return Children.map(children, child =>
            cloneElement(child, { ...this.props, data })
        );
    }
}

L'abonné obtient son état auprès de l'éditeur, qui met en cache les rubriques:

const eventEmitter = new EventEmitter();

const publisher = {
  subscribe: function (eventName, cache) {
    eventEmitter.on(eventName, data => {
      this.cache[eventName] = cache(data);
    });
  },
  unsubscribe: function (eventName, fn) {
    eventEmitter.off(eventName, fn)
  },
  send: function (eventName, payload) {
    eventEmitter.emit(eventName, payload);
    if (!this.cache[eventName]) {
      this.cache[eventName] = { data: payload };
    }
  },
  getState: function (topic) {
    return this.cache[topic] || {};
  },
  cache: {}
}

Le profileur de composants suggère que cette configuration est rendue très efficacement. De plus, l'état est conservé dans un cache en dehors de React land. Si vous me demandez, c'est à peu près juste Flux avec une torsion. Tes pensées?

Réponses

akash Oct 17 2020 at 12:43

Vous n'êtes subscribe()pas un vrai HOC.

Pourquoi ? (se concentrer sur les mots gras)

  • HOC est une fonction pure qui renvoie un composant conteneur qui encapsule le composant d'origine.
  • subscribe()est juste un composant wrapper qui enveloppe juste le composant d'origine et le retourne.

Voici une réponse détaillée: https://stackoverflow.com/a/64178585/8323442