Migración de React Router: de la versión 6 a la 5 en proyectos empresariales
Resumen
¿Cuál es el reto al trabajar con paquetes desactualizados en empresas?
Cuando nos enfrentamos al mundo real de una empresa, no siempre trabajamos con las tecnologías más recientes. Así que, ¿qué ocurre cuando te encuentras con versiones antiguas de ciertas herramientas, como React Router DOM 5, en lugar de las actuales que usaste para tus proyectos personales? La respuesta, según la experiencia compartida, es que debes adaptarte al entorno laboral que encuentres. Es crucial entender que, aunque el aprendizaje y el impulso por estar al día son importantes, muchas empresas aún operan con configuraciones que garantizan estabilidad y continuidad en sus aplicaciones.
¿Qué implica regresar a versiones anteriores de paquetes?
Challenge personal y profesional: Volver a usar versiones anteriores nos desafía a encontrar soluciones con herramientas que quizás no sean tan avanzadas como las actuales.
Contexto empresarial: Las empresas priorizan la estabilidad y eficiencia de sus operaciones diarias sobre estar a la vanguardia tecnológica.
Rendimiento y compatibilidad: Algunas veces, la migración hacia versiones más nuevas no proporciona un beneficio directo en el rendimiento ni mejora la compatibilidad de la API que justifique el cambio.
¿Cómo abordar el trabajo en entornos tecnológicos anteriores?
Trabajar en un entorno que no siempre está a la vanguardia puede parecer un retroceso, pero tiene su propio valor y lecciones:
Flexibilidad y adaptabilidad: Ser capaz de adaptarse a diferentes versiones y tecnologías es una habilidad valiosa.
Concentrarse en la funcionalidad: Es esencial mantener el enfoque en resolver problemas reales y proporcionar valor inmediato, más que obsesionarse con actualizar herramientas.
Justificación de cambios tecnológicos: Ante propuestas de actualización, siempre tendrás que justificar cómo el cambio aporta valor, como mejor rendimiento o funcionalidad crucial para el usuario.
¿Por qué podría no ser prioritario actualizar las dependencias?
En un caso hipotético donde se plantee la migración de React Router DOM de la versión 5 a la 6, la discusión puede centrarse en aspectos prácticos:
Costo-beneficio: ¿Realmente el beneficio justifica el esfuerzo y tiempo de hacer la actualización?
Impacto en el usuario: Mejorar las características que impactan directamente al usuario puede ser más prioritario que internalizar novedades de nuevas versiones.
Esfuerzo en el cambio: La migración implica no solo cambiar el package.json, sino adaptar todo el código preocupado por las dependencias usadas.
Este es un escenario muy común en empresas consolidadas y describe la necesidad de balancear entre innovar y preservar la estabilidad de los productos ya funcionando. Y aunque pueda parecer restrictivo, este tipo de retos nos preparan para enfrentar situaciones similares en entornos laborales reales, donde la adaptabilidad y la habilidad para trabajar con diversas versiones se vuelven cruciales. Por lo tanto, nunca subestimes el valor de dominar versiones anteriores, porque te dota de una perspectiva más amplia y flexible.
Los cambios que hice para que funcionará con React router dom versión 5 fueron:
En el archivo App.js
En el archivo TodoForm/index.js
En el archivo HomePage.js
Hace poco me paso que estaba actualizando esta libreria a la v6 en un proyecto grande. Estaba haciendo todos los cambios hasta que me tope que en la ultima version habian eliminado un feature que estabamos usando para bloquear la navegacion.
Hablo de usePrompt y useBlocker.
Esto se usa si por ejemplo el usuario esta completando un formulario y antes de guardar los cambios quiere navegar a otra ruta. Nosotros le mostramos un modal de confirmacion antes de hacer la navegacion si tiene cambios. Entonces usamos ese feature. Pero en la version 6 no se puede (o no descubri como) hacerlo.
muy válido para entender por qué no van a actualizar el curso práctico de React
Hecho! Este me parece uno de los mejores retos del curso, sin él nunca me hubiera probado una versión anterior de nada.
.
La mayoría de cambios son superficiales, cambian las rutas y algunos hooks por otros. Me voy a explayar en el reto de los query params, que sí es más complicado.
.
Anteriormente, se podía usar el hook useSearchParams. Lamentablemente este no está en la V5. Lo que hice después de muuuucha investigación fue hacer un hook propio (fuertemente inspirado en respuestas de StackOverflow)
.
Se siente tan fácil una vez terminado... no fue directo, pero fue muy entretenido y un gran desafío.
Hola Bramucci! el nombre del ++useSearchParams++ en la version 5 sería ++URLSearchParams++ por lo que estube investigando
Documentación de React Router DOM V5
Documentación aquí
React Router V5 vs V6
Documentación de React Router
No fue tan sencillo como parece.. Batalle mucho con el reto de TodoSearch.
Dejo el codigo por si a alguien le sirve de apoyo.
functionTodoSearch({ searchValue, setSearchValue, loading }){const history =useHistory();constonSearchValueChange=(event)=>{const value = event.target.value;setSearchValue(value); history.push({search: value });//modificar la url y guarda todo en location};const param = history.location.search.slice(1);// usar el metodo slice para quitar '?' al inicio del stringconst searchParam =decodeURI(param)?? searchValue;// decodeURI es una funcion de javascript ayuda a decodificar la url.return(<input
className="TodoSearch" placeholder="Cebolla" value={searchParam} onChange={onSearchValueChange} disabled={loading}/>);}
Lo único raro, fue switch en vez de routes y route.
Esta clase la tomé a finales de Octubre de 2025 cuando React ya reportaba su versión 19.1 y React Router ofrecía su versión 7.9. Además, utilicé a Vite como bundler en lugar de CRA que es el que se utiliza en los vídeos del curso.
A quienes les interese y utilicen un entorno similar, les confirmo que para usar React Router en su versión 5, es necesario degradar el React a la versión 17 e incluir la importación del objeto completo de React al inicio de cada archivo con extensión jsx, para que la renderización no genere errores en la consola del navegador.
Los demás ajustes están registrados en el siguiente repositorio:
Dejo el commit y la rama para la migracion bien explicados en inglés:
- My english version branch and commit:
Branch name:
react-router-v5
Commit message:
feat(routing): implement React Router v5
- Replace useNavigate with useHistory for navigation
- Update URL search params handling with useLocation
- Maintain route state passing between components
- Keep nested route component structure in App.js
- Preserve existing route params functionality
- Handle todo edit state passing through history.push
Breaking changes:
- Remove useSearchParams in favor of URLSearchParams API
- Update navigation methods across components
Hola bueno los compañeros ya compartieron como cambiar a la version 5 utilizando distintos valores como Swich y history.
Yo vengo a compartir mi solución sobre el search🥸, ya que estuve como loco un día entero jajaj.
Lo que hice fue crear la ruta dinámica con el path="/search/:slug?" que quiere decir que puede recibir un slug o no para que no se rompa todo.
Despues en el evento del Input se debe encargar de actualizar el estado (que renderiza las tareas encontradas) y tambien actualizar la ruta de esta forma :
{
history.push({
pathname:"/search",
search:event.target.value, }) //ruta
setSearchValue(event.target.value)//estado
}
Porque este quilombo de pasar el estado de search?
Para asi poder hacer una validación si el usuario entra con la url de una busqueda.
Aclaro que envolví esta validación en un Efectt que su ciclo de vida es de una ejecucion, porque me pareció lo mas adecuado para un caso real...
La ventaja es que no se renderice cada vez que el usuario escriba una letra, solo si el usuario entre por primera vez con esa busqueda realizada. Siento que no es óptimo que se ejecute imaginando que fuesen consultas a una API por ejemplo.
La desventaja es que si decide navegar por la URL no funcionará al menos que refresque la pagina.
Les dejo el código, si hay algun error háganmelo saber quizas hice cualquiera 😂.
.;
//APP<Routeexactpath="/search/:slug?"><HomePage/></Route>//TodoSearchfunctionToDoSearch({ searchValue, setSearchValue, loading }){constlocation=useLocation();const history =useHistory();React.useEffect(()=>{if(location.search){setSearchValue(location.search.replace("?",""))console.log("EJECUCION")}},[])//aca podria poner que dependa de searchValue pero tendría un costo alto de rendimientoreturn(<inputplaceholder="Busca Tareas"className="search-input"value={searchValue}onChange={(event)=>{ history.push({pathname:"/search",search:event.target.value,});setSearchValue(event.target.value)}}disabled={loading}/>)}
Reto: TODO Machine con React Router DOM 5
.
El reto es replicar el mismo comportamiento de la aplicación con React Router DOM 5.
.
Para ello vamos a mantener la misma estructura de carpetas que utilizamos para la aplicación con React Router DOM 6 pero modificando algunos archivos donde algunos hooks o componentes proporcionados por este no existan o funcionen distintamente en la versión 5.
.
Para este reto hemos utilizado la versión 5.3.4 de React Router DOM.
.
"react-router-dom":"^5.3.4"
.
En src/routes/App.js ya no se usa el componente Routes de la versión 6, sino que se usa Switch. En lugar de element para definir el componente que se debe renderizar ahora utilizamos component.
.
Otra diferencia es que en la versión 6 element acepta un elemento de React directamente como <HomePage />, mientras que en la versión 5 component solo acepta una referencia a un componente como HomePage.
.
En la versión 6, es más directo usar el prop element={<p>Not Found</p>} con JSX, mientras que en la versión 5, se utiliza una función que retorna JSX component={() => <p>Not Found</p>}.
.
en la versión 6 Routes entiende de forma implícita cuándo una ruta es exacta, no se usa exact porque el enrutador lo maneja automáticamente, mientras que en la versión 5 usa exact para asegurar que la ruta exacta se corresponda.
.
.
En el componente HomePage, vamos a remplazar useNavigate por useHistory para la navegación con React Router DOM 5. El hook useHistory utiliza un método push para navegar a una ruta.
.
Ambas versiones permiten pasar un estado a través de la navegación, en la versión 6 con navigate('/edit/' + todo.id, { state: { todo } }) y en la versión 5 con history.push('/edit/' + todo.id, { todo }).
.
importReactfrom"react";import{ useHistory }from"react-router-dom";import{ useTodos }from"../useTodos";import{TodoHeader}from"../../ui/TodoHeader";import{TodoCounter}from"../../ui/TodoCounter";import{TodoSearch}from"../../ui/TodoSearch";import{TodoList}from"../../ui/TodoList";import{TodoItem}from"../../ui/TodoItem";import{TodosError}from"../../ui/TodosError";import{TodosLoading}from"../../ui/TodosLoading";import{EmptyTodos}from"../../ui/EmptyTodos";import{CreateTodoButton}from"../../ui/CreateTodoButton";import{ChangeAlert}from"../../ui/ChangeAlert";functionHomePage(){const history =useHistory();const{ state, stateUpdaters }=useTodos();const{ error, loading, searchedTodos, totalTodos, completedTodos, searchValue,}= state;const{ completeTodo, deleteTodo, setSearchValue, sincronizeTodos,}= stateUpdaters;return(<React.Fragment><TodoHeaderloading={loading}><TodoCountertotalTodos={totalTodos}completedTodos={completedTodos}/><TodoSearchsearchValue={searchValue}setSearchValue={setSearchValue}/></TodoHeader><TodoListerror={error}loading={loading}totalTodos={totalTodos}searchedTodos={searchedTodos}searchText={searchValue}onError={()=><TodosError/>}onLoading={()=><TodosLoading/>}onEmptyTodos={()=><EmptyTodos/>}onEmptySearchResults={(searchText)=>(<p>No hay resultados para {searchText}</p>)}>{(todo)=>(<TodoItemkey={todo.id}text={todo.text}completed={todo.completed}onEdit={()=>{ history.push("/edit/"+ todo.id,{ todo });}}onComplete={()=>completeTodo(todo.id)}onDelete={()=>deleteTodo(todo.id)}/>)}</TodoList><CreateTodoButtononClick={()=> history.push("/new")}// setOpenModal={setOpenModal}/><ChangeAlertsincronize={sincronizeTodos}/></React.Fragment>);}export{HomePage};
.
En TodoForm es hacer lo mismo, remplazar useNavigate por useHistory.
.
importReactfrom'react';import{ useHistory }from'react-router-dom';import'./TodoForm.css';functionTodoForm(props){const history =useHistory();const[newTodoValue, setNewTodoValue]=React.useState(props.defaultTodoText||'');constonChange=(event)=>{setNewTodoValue(event.target.value);};constonCancel=()=>{ history.push('/');};constonSubmit=(event)=>{ event.preventDefault(); props.submitEvent(newTodoValue); history.push('/');};return(<formonSubmit={onSubmit}><label>{props.label}</label><textareavalue={newTodoValue}onChange={onChange}placeholder="Cortar la cebolla oara el almuerzo"/><divclassName="TodoForm-buttonContainer"><buttontype="button"className="TodoForm-button TodoForm-button--cancel"onClick={onCancel}> Cancelar
</button><buttontype="submit"className="TodoForm-button TodoForm-button--add">{props.submitText}</button></div></form>);}export{TodoForm};
.
Finalmente, en TodoSearch es donde se dio el mayor cambio, puesto que habíamos implementado búsqueda por navegación con useSearchParams. Sin embargo, este hook no existe en la versión 5 de React Router DOM.
.
Lo que se hizo fue utilizar useHistory y useLocation para obtener la propiedad search de useLocation, luego cada que escribamos algo en el input de búsqueda vamos a navegar con useHistory hacia el pathname / con ?search=${event.target.value} concatenado con lo que escribamos en dicho input.
.
Finalmente, en un useEffect revisamos que si el search empieza con ?search= significa que hemos accedido directamente a la búsqueda por la url, así que recuperamos el valor de la búsqueda, lo decodificamos en caso de que la búsqueda contuviera espacios representados como %20 y cambiamos el estado de searchValue mediante setSearchValue.
.
De esta manera obtendremos la búsqueda por navegación tanto por el input de búsqueda, como desde la url.
.
Aquí les comparto mi repositorio en github en donde la rama main está con react router dom v6, mientras que la rama proyecto-react-router-dom-5, la aplicación está con react router dom v5.
Repositorio:
curso-react-router-proyecto2
Muy bien el reto es un caso de un sistema Legacy, pero El problema de no actualizar a React Router DOM 6 si tenias React Router DOM 5 a la fecha esta sin soporte pero la realidad es que si te topas tenes que insistir de todos modos porque eso de quedarse sin soporte es parte de la deuda tecnica que se acumula, es mejor ir actualizando o te quedaras obsoleto llegando a un punto que tu paquete desaparecio de NPM vease "Incidente Left-Pad NPM 2016" porque el autor desidio dar de baja un paquete sin saber que medio mundo lo usaba.
Les dejo "ToDos" los cambios que hice por si alguien también quiere tirar la toalla, para que no la tire!!!
Como yo casi quería dejar el curso en este punto y luego me acordé del tema de PASITO a PASITO, una cosa a la vez y tener tambien visión global de las cosas y que esto esta hecho por personassssssss. No robots despreciables sin compasión. Me asocie con San ChatGPT, me encomende a los dioses de la internet que siempre usa el profe y empecé a cambiar lo que fui identificando como necesario:
Pasos para el cambio:
Ejecutar:
npm uninstall react-router-dom
Ejecutar:
npm install react-router-dom@5
Reemplazar:
Routes por Switch
Reemplazar:
<Route path="/xxx" element={< xxx />} /> por
<Route path="/xxx" component={ xxx } />
Reemplazar:
<Route path="/xxx" element={<Navigate to="/xxx" />} /> por
<Redirect from="/xxx" to="/xxx" />
Reemplazar:
const navigate = useNavigate() por cconst history = useHistory();
Reemplazar:
import { Routes, Navigate, useNavigate } from 'react-router-dom'; por
import { Switch, Redirect, useHistory } from 'react-router-dom';
Reemplazar:
navigate('/xxx'); por
history.push('/xxx');
Remplazar:
onEdit={() => navigate(/edit/${toDo.id}, {state: toDo.text })} por
Añadir :
import { useHistory } from "react-router-dom";
const history = useHistory()
Los Remplazos que más me sacaron la piedrunchis..., el tiempo y mas me hicieron sufrir: (me ayudo chatGPT 😉)
En el componente hijo de Editar:
Reemplazar:
if (location.state) {
previousText = location.state} por
if (location.search) {
const params = new URLSearchParams(location.search) previousText = params.get('text')}
Si después de todo esto sigue fallando, le mandas a chatGPT todo tu código y que te acabe de ajustar la solución. Hay que mandar homepage, las dos rutas de edit y add, la app.js y el código de useToDos por si acaso. Suerte!
Les dejo "ToDos" los cambios que hice por si alguien también quiere tirar la toalla, para que no la tire!!!
Como yo casi quería dejar el curso en este punto y luego me acordé del tema de PASITO a PASITO, una cosa a la vez y tener tambien vision global de las cosas y que esto esta hecho por personassssssss. No robots despreciables sin compasion. Me asocie con San ChatGPT, me encomende a los dioses de la internet que siempre usa el profe y empecé a cambiar lo que fui identificando como necesario:
Pasos para el cambio:
Ejecutar:
npm uninstall react-router-dom
Ejecutar:
npm install react-router-dom@5
Reemplazar:
Routes por Switch
Reemplazar:
<;Route path="/xxx" element={< xxx />} /> por
<Route path="/xxx" component={ xxx } />
Reemplazar:
<Route path="/xxx" element={<Navigate to="/xxx" />} /> por
<Redirect from="/xxx" to="/xxx" />
Reemplazar:
const navigate = useNavigate() por
const history = useHistory();
Reemplazar:
import { Routes, Navigate, useNavigate } from 'react-router-dom'; por
import { Switch, Redirect, useHistory } from 'react-router-dom';
Reemplazar:
navigate('/xxx'); por history.push('/xxx');
Remplazar:
onEdit={() => navigate(/edit/${toDo.id}, {state: toDo.text })} por
Añadir :
import { useHistory } from "react-router-dom";
const history = useHistory()
Los Remplazos que más me sacaron la piedrunchis..., el tiempo y mas me hicieron sufrir: (me ayudo chatGPT 😉)
En el componente hijo de Editar:
Reemplazar:
if (location.state) {
previousText = location.state} por
if (location.search) {
const params = new URLSearchParams(location.search) previousText = params.get('text')}
Si después de todo esto sigue fallando, le mandas a chatGPT todo tu código y que te acabe de ajustar la solución. Hay que mandar homepage, las dos rutas de edit y add, la app.js y el código de useToDos por si acaso. Suerte!
Les dejo "ToDos" los cambios que hice por si alguien también quiere tirar la toalla, para que no la tire!!!
Como yo casi quería dejar el curso en este punto y luego me acordé del tema de PASITO a PASITO, una cosa a la vez y tener tambien vision global de las cosas y que esto esta hecho por personassssssss. No robots despreciables sin compasion. Me asocie con San ChatGPT, me encomende a los dioses de la internet que siempre usa el profe y empecé a cambiar lo que fui identificando como necesario:
Pasos para el cambio:
Ejecutar: npm uninstall react-router-dom
Ejecutar: npm install react-router-dom@5
Reemplazar:
Routes por Switch
Reemplazar:
<Route path="/xxx" element={< xxx />} /> por
<Route path="/xxx" component={ xxx } />
Reemplazar:
<Route path="/xxx" element={<Navigate to="/xxx" />} /> por
<Redirect from="/xxx" to="/xxx" /> - Reemplazar:
const navigate = useNavigate() por const history = useHistory();
Reemplazar:
import { Routes, Navigate, useNavigate } from 'react-router-dom'; por
import { Switch, Redirect, useHistory } from 'react-router-dom';
Reemplazar:
navigate('/xxx'); por history.push('/xxx');
Remplazar:
onEdit={() => history.push(/edit/${toDo.id}`, {state: toDo.text }) por
Añadir :
import { useHistory } from "react-router-dom";
const history = useHistory()
Los Remplazos que más me sacaron la piedrunchis..., el tiempo y mas me hicieron sufrir: (me ayudo chatGPT 😉)
En el componete hijo de Editar:
Reemplazar:
if (location.state) {
previousText = location.state} por
if (location.search) {
const params = new URLSearchParams(location.search) previousText = params.get('text')}
Si despues de todo esto sigue fallando, le mandas a chatGPT todo tu codigo y que te acabe de ajustar la solucion. Hay que mandar homepage, las dos rutas de edit y add, la app.js y el codigo de useToDos por si acaso.
Suerte!
Recuerdo la primera vez que me puse aprender React, en ese tiempo aún tenían una batalla campal por ver que router usar (Solo que yo no lo sabía), el router estaba, solo estaba; cuando buscabas información sobre el tema terminabas más cerca de la batalla campal que de la solución; ahora es la guerra por los routers está digamos que en relativa paz; este reto no es dificil, solo que volver a esos tiempos es * Recuerdos de Vietnam * recordar el como intentaba usar cosas de las que no sabía lo suficiente, que ahora veo y es más facil, este reto me recordó un montón esos días... Así que gracias por el ataque de nostalgia, realmente he aprendido mucho desde esos días