4 votes

Composant d'en-tête React avec créneaux horaires

Je travaille sur une application React qui a quelques commutateurs react router imbriqués et une logique non triviale impliquant le changement du contenu de l'en-tête en fonction de la page actuellement affichée.

J'ai déjà vu deux solutions à ce problème :

  1. A <Header/> qui sait absolument tout sur chaque route possible et sur l'état de l'application.
  2. Mise en page plate : chaque page rend sa propre instance de <Header />

À mon avis, les deux solutions sont nulles. Je pense que l'en-tête doit être nourri de merde et maintenu dans l'obscurité. Tout ce qu'il doit faire est de rester en haut de la page. Inspiré par React Helmet, je veux faire une solution déclarative à ce problème.

Supposons que nous ayons un en-tête avec trois sections : gauche, droite et centre. A la racine de notre application, nous rendons quelque chose comme

return (
    <HeaderLeft>
        <Logo />
    </HeaderLeft>
    ...
)

Qui, comme prévu, affiche un logo.

Quelque part plus profondément dans la hiérarchie de l'arbre des composants, nous avons une NestedPage qui doit changer ce qui est affiché dans la section gauche de l'en-tête. Nous faisons donc simplement

return (
    <HeaderLeft>
        <SomethingElse />
    </HeaderLeft>
    ...
)

Le truc c'est que quand on navigue loin de la NestedPage nous devons remettre dans le <HeaderLeft/> ce qui était là avant que nous arrivions au NestedPage .

J'ai trouvé une solution qui fonctionne plus ou moins bien et qui fait appel au contexte de réaction et à la pile :

https://codesandbox.io/s/great-mendeleev-0b5y4?file=/src/MainPage.js

Mais ces choses dépendent fortement de l'ordre d'exécution de useEffects.

La question

Quelqu'un a-t-il une solution idiomatique/une idée d'un modèle/un lien vers la façon d'obtenir le résultat souhaité ?

0voto

magom001 Points 67

Ayant parcouru le code source de React Helmet, j'ai fini par utiliser ce petit bijou

Il fait exactement ce dont j'ai besoin : il collecte de manière synchrone les props de toutes les instances dans l'ordre de rendu.

Ainsi, nous pouvons obtenir le dernier children rendu :

function reducePropsToState(props) {
  return props[props.length - 1];
}

La partie délicate était de laisser le Header Les composants savent ce qu'il faut rendre. Heureusement, nous sommes tous des ingénieurs en logiciel et React fonctionne en javascript.

J'ai créé un simple singleton observable :

class Observable {
  constructor() {
    this.value = null;
    this.subscribers = [];
    this.raf = null;
    this.notify = this.notify.bind(this);
  }

  subscribe(s) {
    this.subscribers.push(s);
    s(this.value);
  }

  setValue(c) {
    this.value = c || null;
    cancelAnimationFrame(this.raf);
    this.raf = requestAnimationFrame(this.notify);
  }

  notify() {
    this.subscribers.forEach((subscriber) => {
      subscriber(this.value);
    });
  }
}

Dans le composant d'en-tête, nous nous abonnons à cet observable et mettons à jour l'état local :

useLayoutEffect(() => {
    contentObservable.subscribe((v) => {
      setContent(v);
    });
  }, []);

Démonstration dans codesandbox

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X