es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Cómo detener la obtención de datos 2 veces en SSR (primero en el lado del servidor y luego en el lado del cliente)?

Estoy implementando el renderizado en el servidor en una aplicación SPA de React y Redux. Para el SSR, obtengo los datos en el servidor y creo la página utilizando el método renderToString, luego en el lado del cliente, obtengo los datos en useEffect como se muestra en el siguiente ejemplo.

El problema ahora es que después de obtener los datos en el servidor, el cliente vuelve a llamar a fetchRequestQuery(dispatch) en useEffect. Por lo tanto, está llamando a la misma API 2 veces, lo cual es incorrecto. No puedo quitar useEffect ya que cuando navego por la página (react router Link), los datos deben obtenerse solo en el lado del cliente para SPA.

Entonces, ¿cómo puedo desactivar la llamada a la API en useEffect en el lado del cliente cuando abro la página por primera vez, pero cuando navego por la página, debe funcionar de manera normal y realizar la llamada a la API en useEffect?

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchRequestQuery } from '../actions';

const loadData = dispatch => (
  fetchRequestQuery(dispatch)
);

const Content = () => {
  const dispatch = useDispatch();
  useEffect(() => {
    loadData(dispatch);
  }, []);

  const { request } = useSelector(state => state);
  return (
    <span>{JSON.stringify(request)}</span>
  );
};

export default {
  component: Content,
  loadData,
};

“`import express from ‘express’;
import { matchRoutes } from 'react-router-config';
import Routes from './Routes';
import renderer from './helpers/renderer';
import createStore from './store';

const app = express();
app.use(express.static('dist'));

app.get('*', (req, res) => {
const store = createStore();
const { dispatch } = store;
const routes = matchRoutes(Routes, req.path);
const promises = routes.map(
({ route }) => (route.loadData ? route.loadData(dispatch) : null),
);
Promise.all(promises).then(() => {
const content = renderer(req, store);
res.send(content);
});
});

const port = process.env.PORT || 3001;
app.listen(port, () => {
console.log(<code>Escuchando en el puerto: ${port}</code>);
});
“`

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Para desactivar la llamada API redundante en useEffect en el lado del cliente al abrir la página por primera vez, una solución podría ser establecer una bandera en el estado de la aplicación para indicar que los datos ya han sido obtenidos en el servidor. Esta bandera podría ser establecida cuando el servidor obtiene los datos y se pasa al cliente a través del estado inicial enviado con la respuesta HTML.

    Luego, en el hook useEffect, puedes verificar si la bandera está establecida antes de hacer la llamada API. Si la bandera no está establecida, significa que los datos ya han sido obtenidos en el servidor y no es necesario obtenerlos nuevamente en el lado del cliente. Si la bandera está establecida, entonces es seguro proceder con la llamada API como de costumbre.

    Aquí tienes un fragmento de código actualizado para demostrar este enfoque:

    import React, { useEffect } from ‘react’;
    import { useSelector, useDispatch } from ‘react-redux’;
    import { fetchRequestQuery } from ‘../actions’;

    const loadData = dispatch => (
    fetchRequestQuery(dispatch)
    );

    const Content = () => {
    const dispatch = useDispatch();
    const { request, isServerRendered } = useSelector(state => state);

    useEffect(() => {
    if (!isServerRendered) {
    loadData(dispatch);
    }
    }, [dispatch, isServerRendered]); // dependencias actualizadas para incluir isServerRendered

    return (
    {JSON.stringify(request)}
    );
    };

    export default {
    component: Content,
    loadData,
    };

    y el código modificado del servidor:

    import express from ‘express’;
    import { matchRoutes } from ‘react-router-config’;
    import Routes from ‘./Routes’;
    import renderer from ‘./helpers/renderer’;
    import createStore from ‘./store’;

    const app = express();
    app.use(express.static(‘dist’));

    app.get(‘*’, (req, res) => {
    const store = createStore();
    const { dispatch } = store;
    const routes = matchRoutes(Routes, req.path);
    const promises = routes.map(
    ({ route }) => (route.loadData ? route.loadData(dispatch) : null),
    );
    Promise.all(promises).then(() => {
    const isServerRendered = true;
    const content = renderer(req, store, isServerRendered); // pasar la bandera al renderizador
    res.send(content);
    });
    });

    const port = process.env.PORT || 3001;
    app.listen(port, () => {
    console.log(Escuchando en el puerto: ${port});
    });

    Al establecer la bandera isServerRendered en verdadero y pasarla al cliente como parte del estado inicial, podemos usarla para realizar condicionalmente la llamada API en useEffect en el lado del cliente. Ten en cuenta que deberás actualizar tu tienda de Redux y la función de renderización para manejar la nueva bandera en el estado y las props respectivamente.

Comments are closed.