¿Cómo puede react-router
manejar adecuadamente 404 páginas para contenido dinámico en una aplicación Universal?
Digamos que quiero mostrar una página de usuario con una ruta como '/user/:userId'
. Tendría una configuración como esta:
<Route path="/"> <Route path="user/:userId" component={UserPage} /> <Route path="*" component={NotFound} status={404} /> </Route>
Si solicito /user/valid-user-id
, obtengo la página de usuario.
Si solicito /foo
, obtengo un 404 adecuado.
Pero, ¿qué sucede si solicito /user/invalid-user-id
. Al obtener los datos del usuario, me daré cuenta de que este usuario no existe. Entonces, lo correcto para hacer las costuras es:
¿¿Cómo puedo hacer eso?? Parece un comportamiento muy estándar. Me sorprende no encontrar ningún ejemplo...
Editar:
Parece que no soy el único que lucha con eso. Algo como esto ayudaría mucho: https://github.com/ReactTraining/react-router/pull/3098
Como mi aplicación no se lanzará pronto, decidí esperar para ver qué ofrece la próxima versión del enrutador de reacción...
En primer lugar, cree una función de middleware para la devolución de llamada onEnter
, de modo que esto sea viable para las promesas de redux:
import { Router, Route, browserHistory, createRoutes } from "react-router"; function mixStoreToRoutes(routes) { return routes && routes.map(route => ({ ...route, childRoutes: mixStoreToRoutes(route.childRoutes), onEnter: route.onEnter && function (props, replaceState, cb) { route.onEnter(store.dispatch, props, replaceState) .then(() => { cb(null) }) .catch(cb) } })); } const rawRoutes = <Route path="/"> <Route path="user/:userId" component={UserPage} onEnter={userResolve.fetchUser} /> <Route path="*" component={NotFound} status={404} /> </Route>
Ahora, en esta función onEnter
, puede trabajar directamente con la tienda redux. Por lo tanto, podría enviar una acción que tenga éxito o falle. Ejemplo:
function fetch(options) { return (dispatch) => { return new Promise((resolve, reject) => { axios.get('<backend-url>') .then(res => { resolve(dispatch({type: `CLIENT_GET_SUCCESS`, payload: res.data})) }) .catch(error => { reject(dispatch({type: `CLIENT_GET_FAILED`, payload: error})); }) } }) } } let userResolve = { fetchUser: (dispatch, props, replace) => { return new Promise((next, reject) => { dispatch(fetch({ user: props.params.user })) .then((data) => { next() }) .catch((error) => { next() }) }) } }
Siempre que falle la promesa de resolución, el enrutador de reacción buscará automáticamente el siguiente componente que podría generar para este punto final, que en este caso es el componente 404.
Entonces no tendría que usar replaceWith
y su URL se mantendrá.
Si no está utilizando la representación del lado del servidor, no sería posible devolver 404 antes de que se procese la página. Deberá verificar la existencia del usuario en algún lugar de cualquier manera (en el servidor o mediante AJAX en el cliente). El primero no sería posible sin la representación del lado del servidor.
Un enfoque viable sería mostrar la página 404 por error de la Promesa.
Probé mi solución en un proyecto que estoy haciendo que usa Server Side Rendering y react-router y funciona allí, así que les diré lo que hice.
Cree una función en la que validará una identificación. Si la identificación es válida, devuelva la página con el usuario con el componente adecuado. Si la identificación no es válida, devuelva la página con 404.
Ver el ejemplo:
// Rutas.jsx
function ValidateID(ID) { if(ID === GOOD_ID) { return ( <Route path="/"> <Route path="user/:userId" component={UserPage} /> <Route path="*" component={NotFound} status={404} /> </Route> ); } else { return ( <Route path="/"> <Route path="user/:userId" status={404} component={Page404} /> <Route path="*" component={NotFound} status={404} /> </Route> ); }
// Enrutador.jsx
<Router route={ValidateID(ID)} history={browserHistory}></Router>
Esto debería funcionar con la representación del lado del servidor como lo hizo en mi proyecto . No utiliza Redux.