Configuración básica:
1) La aplicación es React y Redux,
2) La aplicación es atendida por un NGINX frontal que sirve archivos estáticos como html, imágenes y, por supuesto, la propia aplicación. También reenvía todas las solicitudes relevantes (sockets web y/o AJAX) al back-end (phoenix/elixir).
3) Los usuarios deben autenticarse. Estoy usando la biblioteca redux-oidc, que es solo del lado del cliente y funciona bien.
4) Después de que el usuario inicia sesión es cuando no sé qué hacer a continuación.
Preguntas):
1) No puedo enviar el estado junto con la primera solicitud porque no sé quién es el usuario y, por lo tanto, no sé qué estado enviar. Mientras tanto, la aplicación ya se inició (se creó una tienda vacía, se muestra el componente de inicio de sesión),
2) Después de que el usuario inicia sesión, no puedo mostrar nada (como la barra de navegación específica del usuario, la línea de tiempo, el buzón) Tengo que saturar la tienda y dejar que la reacción haga su trabajo. ¿Qué enfoque debo tomar?
3) La representación del servidor está fuera de servicio porque a) No estoy usando Node y la representación de componentes de reacción usando el marco elegido es desordenada y complicada en el mejor de los casos y b) No podré exportar la aplicación a NGinx ya que solo sirve activos estáticos y no se ejecuta ninguna lógica de servidor allí. En teoría, podría deshacerme de NGinx, tener un inicio de sesión basado en servidor en el servidor API y enviar HTML junto con el estado JSON que podría usarse para representar la aplicación en el cliente. Sin embargo, NGinx no solo sirve activos estáticos, sino que también equilibra la carga de algunas instancias y, por lo tanto, deshacerme de él no es algo que quiera hacer.
Cualquier consejo sería apreciado.
La hidratación del estado después de la creación de la tienda se puede lograr mediante la creación de un reductor principal que pueda pasar por alto los reductores de nivel superior y reemplazar todo el estado.
Los reductores son funciones que obtienen el estado actual, lo combinan con la carga útil de una acción y devuelven un nuevo estado. Por lo general, el reductor principal es una combinación de todos los reductores superiores que usan combineReducers , y el estado es la combinación de piezas de estado devueltas por los reductores de nivel superior.
Sin embargo, el reductor principal puede reaccionar directamente a las acciones. Si el reductor principal recibe una determinada acción ( hydrate ), en lugar de llamar a los reductores combinados, devuelve la carga útil de la acción (el estado guardado). Otras acciones se pasan a los reductores combinados.
const mainReducer = (state = {}, action) => action.type === 'hydrate' ? action.payload // hydrate the state : reducers(state, action); // create new state by using combined reducersEjemplo de trabajo:
const { combineReducers, createStore } = Redux; const people = (state = [], action) => action.type === 'people' ? [...state, action.payload] : state; const items = (state = [], action) => action.type === 'items' ? [...state, action.payload] : state; const reducers = combineReducers({ people, items }); const mainReducer = (state = {}, action) => action.type === 'hydrate' ? action.payload : reducers(state, action); const store = createStore(mainReducer); store.subscribe(() => console.log(store.getState())); store.dispatch({ type: 'people', payload: 5 }); store.dispatch({ type: 'items', payload: 'green' }); store.dispatch({ type: 'hydrate', payload: { people: [20, 30, 50, 100], items: ['green', 'yellow', 'red'] }}); <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.6.0/redux.min.js"></script>Aunque la respuesta aceptada podría funcionar, creo que lo que estás persiguiendo es un antipatrón, después de todo.
Si está realizando la representación del cliente y manejando la autenticación del lado del cliente, lo único que debe enviar a su cliente, en mi opinión, es un <Spinner/> , con cero estado precargado.
Una vez que esté en el cliente, inicie su tienda, realice su autenticación y decida si va a obtener datos y presentar la versión autenticada de la página, o si el usuario aún no está autenticado, muéstrele el formulario de inicio de sesión. Todo a partir de este punto debe manejarse del lado del cliente.
Esto es lo que Redux tiene que decir al respecto:
https://redux.js.org/usage/server-rendering
Por lo tanto, si durante el procesamiento inicial obtuvo el usuario "Schrödinger" (puede estar autenticado o no), lo único que puede suponer con seguridad que se muestra es una rueda giratoria.
OTRA OPCIÓN
Si realmente necesita obtener nuevos datos precargados del servidor en múltiples solicitudes (eso es lo que sucede en las aplicaciones NextJS).
Si va a renderizar previamente cada página (y obtener un nuevo estado en cada cambio de página), puede hacer lo que NextJS sugiere que haga:
Aquí está el ejemplo:
https://github.com/vercel/next.js/tree/canary/examples/with-redux-thunk
Y la parte principal de su código:
import { useMemo } from 'react' import { createStore, applyMiddleware } from 'redux' import { composeWithDevTools } from 'redux-devtools-extension' import thunkMiddleware from 'redux-thunk' import reducers from './reducers' let store function initStore(initialState) { return createStore( reducers, initialState, composeWithDevTools(applyMiddleware(thunkMiddleware)) ) } export const initializeStore = (preloadedState) => { let _store = store ?? initStore(preloadedState) // After navigating to a page with an initial Redux state, merge that state // with the current state in the store, and create a new store if (preloadedState && store) { _store = initStore({ ...store.getState(), ...preloadedState, }) // Reset the current store store = undefined } // For SSG and SSR always create a new store if (typeof window === 'undefined') return _store // Create the store once in the client if (!store) store = _store return _store } export function useStore(initialState) { const store = useMemo(() => initializeStore(initialState), [initialState]) return store }