3
votes

React + Redux + SSR: impossible de trouver le magasin uniquement lors de la navigation directe vers le composant connecté

Je suis relativement nouveau dans SSR avec React et Redux, donc je suppose que je fais une simple erreur qui reflète mon manque de compréhension des frameworks sous-jacents. Cependant, je suis sur le point de passer ma tête à travers un mur, alors voilà. J'espère que quelqu'un pourra m'aider.

Si j'essaye de naviguer directement vers la page racine (/), j'obtiens l'erreur Impossible de trouver "store" dans le contexte de "Connect (Home)" . Dans mon cas, App contient ma logique de navigation partagée et mon contenu global, et (/) rend un composant Home . Si je navigue vers une page qui ne se connecte PAS à Redux (par exemple, les conditions d'utilisation), et ALORS accédez à ma page d'accueil, le rendu est parfait. Je suppose que c'est une sorte de problème de routage côté serveur, mais j'ai lu tout ce que je peux trouver en ligne, essayé ce qui ressemble à toutes les permutations possibles de gestion d'état / magasin / contexte, et je ne peux pas le résoudre. P >

Voici le squelette de mon code.

renderer.js

[...]

export default connect(mapStateToProps)(Home);

[...]

configureStore.js

[...]

export default withCookies(App);

[...]

index.js (client)

const store = configureStore( window.__REDUX_STATE__ || {} );

const AppBundle = (
    <ReduxProvider store={store}>
        <BrowserRouter>
            <CookiesProvider>
                <App />
            </CookiesProvider>
        </BrowserRouter>
    </ReduxProvider>
);

window.onload = () => {
    Loadable.preloadReady().then(() => {
        ReactDOM.hydrate(
            AppBundle,
            document.getElementById('root')
        );
    });
};

app.js ("/" rend le composant Home)

[...]

const createStoreWithMiddleware = compose(applyMiddleware(ReduxThunk))(createStore);

export default function configureStore(initialState = {}) {
    return createStoreWithMiddleware(rootReducer, initialState);
};

home.js

[...]

const modules = [];
const routerContext = {};

const bundle = (
    <Loadable.Capture report={m => modules.push(m)}>
        <ReduxProvider store={store}>
            <StaticRouter location={req.baseUrl} context={routerContext}>
                <CookiesProvider cookies={req.universalCookies}>
                    <App />
                </CookiesProvider>
            </StaticRouter>
        </ReduxProvider>
    </Loadable.Capture>
    );

const html = ReactDOMServer.renderToString(bundle);

if (routerContext.url) {
  redirect(301, routerContext.url);
}

// inject into HTML

[...]


2 commentaires

Ce code est-il disponible dans une sorte de dépôt public? Si c'est le cas, pourriez-vous partager le lien? Merci!


@Josep J'ai temporairement ajouté le code source à un dépôt public: github.com/rahuljaswa/homehub-web . Merci.


3 Réponses :


1
votes

Le magasin est accessible à partir de composants enfants si vous l'exposez via un contexte ou via des accessoires.
Vous pouvez également l'exposer en ajoutant le magasin à l'objet window, mais ce ne serait pas une bonne pratique car chaque visiteur de votre site peut voir et modifier le magasin. (cela peut être utile si vous ne vous souciez pas de la sécurité du magasin redux).
Vous pouvez également utiliser un setter, comme suggéré dans cette réponse: Valeur de l'état Redux globalement

Le fournisseur redux rend le magasin disponible à tous les enfants via le contexte.

Cette balise Provider rend votre composant parent, appelons-la l'application composant qui à son tour rend tous les autres composants à l'intérieur du application.

Voici la partie clé, lorsque nous enveloppons un composant avec le connect () fonction, cette fonction connect () s'attend à voir un composant parent dans la hiérarchie contenant la balise Provider.

Invariant Violation: impossible de trouver "magasin" dans le contexte ou dans les accessoires de "Connect (SportsDatabase)"

Donc, selon la réponse liée, votre composant Home doit avoir un fournisseur ou le magasin lui-même dans son contexte, qui peut être la cause de votre erreur.


2 commentaires

Je ne comprends pas comment cela résoudra le problème. Home est un sous-composant de App et App est encapsulé par ReduxProvider dans les bundles serveur et client ...


En effet, le ReduxProvider permet le stockage de tous les composants enfants. Mais comme le magasin ne peut pas être trouvé à l'intérieur de votre composant, il y a quelque chose qui ne va pas.



0
votes

Côté serveur, vous devez également créer votre magasin dont l'état est pris comme état initial pour le rendu côté client. Du côté client, je peux voir que vous avez fait ceci:

const store = configureStore( window.__REDUX_STATE__ || {} )  

Une chose similaire doit également être faite côté serveur.

Du côté serveur, vous besoin de faire store = createStore (rootReducer) pour créer le magasin qui est utilisé ici dans rendu.js.

Veuillez revenir en arrière pour tout doute ou clarification.


1 commentaires

Je crée le magasin sur le serveur avant de le transmettre à ReduxProvider dans renderer.js.



2
votes

J'ai enfin identifié le problème. J'utilisais un serveur Express pour héberger les pages rendues par le serveur en même temps que le client Node. Malheureusement, le serveur créait une instance de React, tandis que le client créait une autre instance de React. En conséquence, même si le code semblait correct, mon magasin Redux n'était pas transmis dans la hiérarchie des composants pour le même contexte React.

Il m'a fallu une éternité pour déboguer et identifier. Cet article m'a finalement orienté dans la bonne direction ... mais cela ne m'a pas fait tout le chemin car le débogueur React ne fonctionnera pas sur une application rendue par le serveur.

Il existe plusieurs solutions possibles au problème. Dans mon cas, la résolution la plus simple était de consolider toutes mes dépendances entre le client et le serveur, ce qui supprimait implicitement toutes les instances React redondantes.

J'espère vraiment que cette réponse aidera quelqu'un d'autre, car j'ai passé plus d'un mois à essayer de la déboguer une ou deux fois par jour par semaine.


0 commentaires