When I'm on page /orders/:id I click to Link component to navigate another order (/orders/:anotherId) - URL changes but the view is not.
React (18.2.0) changes URL using navigate, but the page will not update (or not rerender, idk, I'm new in React). I tried other topics on Stack, but nothing helped. What's kind of problem?
package.json
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.14",
"@mui/material": "^5.14.14",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.5.1",
"javascript-barcode-reader": "^0.6.9",
"material-react-table": "^1.15.1",
"mobx": "^6.10.2",
"mobx-react-lite": "^4.0.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.17.0",
"react-scripts": "5.0.1",
"reactjs-popup": "^2.0.6",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
AppRouter.jsx
import React, { useContext, useEffect } from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import {observer} from "mobx-react-lite";
import { privateRoutes, publicRoutes } from '../router/index';
import { Context } from '../index';
const AppRouter = () => {
const { store } = useContext(Context);
if (store.isLoading) {
return <div>Загрузка...</div>
}
return (
store.user.email === "admin"
?
<Routes>
{privateRoutes.map(route =>
<Route
key={route.path}
path={route.path}
element={<route.element/>}>
</Route>
)}
{/* <Route path="*" element={<Navigate to='/start' replace />} /> */}
{console.log("private routes")}
</Routes>
:
<Routes>
{publicRoutes.map(route =>
<Route
key={route.path}
path={route.path}
element={<route.element/>}>
</Route>
)}
{/* <Route path="/orders" element={<OrderList />}/> */}
{/*<Route path="*" element={<Navigate to='/login' replace />} />*/}
</Routes>
)
}
export default observer(AppRouter);
index.js
import React, { createContext } from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import Store from "./store/store";
import { BrowserRouter } from 'react-router-dom';
export const store = new Store();
export const Context = createContext({
store,
})
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Context.Provider value={{
store
}}>
<BrowserRouter>
<App />
</BrowserRouter>
</Context.Provider>
);
routes
import RegistrationForm from "../components/RegistrationForm";
import Registration from "../pages/Registration";
import Start from "../pages/Start";
import Orders from "../pages/Orders";
import Login from "../pages/Login";
import OrderById from "../pages/OrderById";
import OrdersInWork from "../pages/OrdersInWork";
export const privateRoutes = [
// {path: '/registration', element: Registration}, // replace all elements to Pages,
{path: '/orders/:id', element: OrderById},
{path: '/start', element: Start},
{path: '/ordersinwork', element: OrdersInWork},
{path: '/orders', element: Orders},
{path: '/login', element: Login}
]
export const publicRoutes = [
{path: '/orders/:id', element: OrderById},
{path: '/start', element: Start},
{path: '/registration', element: Registration},
{path: '/login', element: Login},
]
OrderById component
import React, { useEffect, useState, useMemo, useReducer, useContext } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import OrderService from '../services/OrderService';
import { MaterialReactTable } from 'material-react-table';
import { Box, Button, ListItemIcon, MenuItem, Typography } from '@mui/material';
import { Context } from '../index';
import Popup from 'reactjs-popup';
import 'reactjs-popup/dist/index.css';
import OrderList from "../components/OrderList";
export default function OrderById() {
const { store } = useContext(Context);
const pagination = {
pageSize: 100
}
const [order, setOrder] = useState([]);
const [rowSelection, setRowSelection] = useState({});
let [barcodeParts, setBarcodeParts] = useState('');
const [barcode, setBarcode] = useState({});
const [, forceUpdate] = useReducer(x => x + 1, 0);
const params = useParams();
const [showPopup, setShowPopup] = useState(false);
const [isWaitingButtonClicked, setIsWaitingButtonClicked] = useState(false);
const navigate = useNavigate();
const columns = useMemo(
() => [
{
accessorKey: 'image',
header: 'Изображение',
size: 200,
Cell: ({ cell }) => (
<img
alt="No image"
src={cell.getValue()} />
)
},
{
accessorKey: 'article',
header: 'Артикул',
size: 200
},
{
accessorKey: 'quantity',
header: 'Количество',
size: 200
}
],
[],
);
useEffect(() => {
getOrderById(params.id);
// store.checkOrdersInWork();
}, [])
useEffect(() => {
console.log(rowSelection)
}, [rowSelection])
useEffect(() => {
function handleKeyDown(e) {
if (e.key === 'Enter') {
setBarcode({ value: parseScannedInput(barcodeParts) });
setBarcodeParts('');
return;
}
let newBarcode = barcodeParts.concat(e.key);
setBarcodeParts(newBarcode);
}
document.addEventListener('keydown', handleKeyDown);
// why?
// Don't forget to clean up
return function cleanup() {
document.removeEventListener('keydown', handleKeyDown);
}
}, [barcodeParts]);
useEffect(() => {
if (order.length !== 0) {
const needSelectRowIndex = order.positions.findIndex(position => position.barcode === barcode.value);
console.log(barcode.value)
console.log(needSelectRowIndex);
if (needSelectRowIndex === -1) {
setBarcodeParts('');
setShowPopup(!showPopup);
// alert("Неверный штрихкод!")
// document.getElementByClassName('MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation2 css-80pr5n-MuiPaper-root').focus();
return;
}
const selectedRow = Object.assign(rowSelection, { [needSelectRowIndex]: true })
setRowSelection((selectedRow) => (selectedRow));
console.log(barcode);
console.log(rowSelection);
}
forceUpdate(); // функция, которая ререндерит компонент (если этого не делать, строка выделяется после второго пика или какого-либо другого действия на странице)
}, [barcode])
async function getOrderById(id) {
let orderIsTaken = false;
while (!orderIsTaken) {
try {
// const mappedPosition1 = positions.filter(position => position.assortment?.meta.type !== "service");
// const mappedPosition = positions.filter(position => position.assortment?.meta.href !== "https://api.moysklad.ru/api/remap/1.2/entity/product/9a8d2bbf-8a73-11ec-0a80-0f0d000e5b0e"); // remove Доставка
const response = await OrderService.getOrderById(id);
console.log(response);
// const reformattedData = await reformatDataOrder(response.data);
setOrder(response.data);
orderIsTaken = true;
} catch (e) {
console.log(e);
}
}
}
function parseScannedInput(input) {
const matches = input.match(/Alt(\d+)/g);
if (!matches) return input;
return matches.map((code) => {
const num = code.replace("Alt", "");
return String.fromCharCode(parseInt(num));
}).join('');
}
return (
<div>
{order?.positions
?
<MaterialReactTable columns={columns} data={order.positions}
enableRowSelection
onRowSelectionChange={setRowSelection}
initialState={{pagination}}
state={{ rowSelection }}
renderTopToolbarCustomActions={({ table }) => {
const handleCollectOrder = async () => {
try {
const result = await OrderService.changeOrderStatus(params.id, 'Собрано');
console.log(result);
const removeOrderFromWork = await OrderService.removeOrderFromWork(params.id, store.user.email);
console.log(removeOrderFromWork)
const addSborshikToOrder = await OrderService.addSborshikToOrder(params.id, store.user.email);
alert(`Заказ ${order.name} переведен на статус ${'Собрано'}!`)
navigate('/start')
// перекинуть на другой заказ
// table.getSelectedRowModel().flatRows.map((row) => {
// alert('deactivating ' + row.getValue('name'));
// });
} catch (error) {
alert(`Ошибка! Что-то пошло не так...\n${error.message}`)
}
};
const handleSendToCorrect = async () => {
try {
const result = await OrderService.changeOrderStatus(params.id, 'Корректировка');
console.log(result);
console.log(store.user.id)
const removeOrderFromWork = await OrderService.removeOrderFromWork(params.id, store.user.email);
console.log(removeOrderFromWork)
alert(`Заказ ${order.name} успешно отправлен на корректировку!`)
navigate('/start')
// table.getSelectedRowModel().flatRows.map((row) => {
// alert('activating ' + row.getValue('name'));
// });
} catch (error) {
alert(`Ошибка! Что-то пошло не так...\n${error.message}`)
}
};
const handleToWaitingList = async () => {
// const removeOrderFromWork = await OrderService.removeOrderFromWork(params.id);
// console.log(removeOrderFromWork)
// console.log(order)
const serverRes = await OrderService.moveOrderToWaitingList(order.id);
alert(`Заказ ${order.name} успешно отправлен в лист ожидания!`)
navigate('/start');
// localStorage.removeItem('orderId');
// table.getSelectedRowModel().flatRows.map((row) => {
// alert('contact ' + row.getValue('name'));
// });
};
return (
<div style={{ display: "flex" }}>
<div style={{ display: 'flex', gap: '0.5rem' }}>
<Button
color="success"
disabled={!table.getIsAllRowsSelected()}
onClick={handleCollectOrder}
variant="contained"
>
СОБРАТЬ/УПАКОВАТЬ
</Button>
<Button
color="error"
// disabled={!table.getIsSomeRowsSelected()}
onClick={handleSendToCorrect}
variant="contained"
>
КОРРЕКТИРОВКА
</Button>
<Button
color="primary"
// disabled={true}
onClick={handleToWaitingList}
variant="contained"
>
В ожидание
</Button>
<Button
color="warning"
// disabled={true}
onClick={() => setIsWaitingButtonClicked(true)}
variant="contained"
>
Мои заказы
</Button>
{/* <input type='text' autoFocus></input> */}
</div>
<div style={{ display: "flex", justifyContent: "center", marginTop: 9, marginLeft: 35 }}>Текущий заказ: <b>{order.name}</b></div>
<div style={{ display: "flex", justifyContent: "center", marginTop: 9, marginLeft: 35 }}>Способ доставки: <b>{order.delivery}</b></div>
{/* <div style={{ display: "flex", justifyContent: "center", marginTop: 9, marginLeft: 35 }}>{`Комментарий: ${order.description}`}</div> */}
</div>
);
}}
/>
:
""}
<Popup open={showPopup} onClose={() => setShowPopup(false) }><h4> Неверный штрихкод! </h4></Popup>
<Popup open={isWaitingButtonClicked} onClose={() => setIsWaitingButtonClicked(false) } modal > <OrderList orders={store.ordersInWork} title="Список заказов в ожидании"/> </Popup>
{/* Loading component */}
</div>
)
}
OrderList component
import React from 'react'
import OrderItem from './OrderItem'
export default function OrderList({orders, title}) {
return (
<div>
<h2>{title}</h2>
{orders.map( (order, index) => {
return <OrderItem order={order} key={order.id} index={index}/>
})}
</div>
)
}
OrderItem component
import React from 'react'
import {Link, useNavigate} from 'react-router-dom'
import MyButton from "../UI/MyButton/MyButton";
import OrderService from "../services/OrderService";
export default function OrderItem(props) {
const navigate = useNavigate();
const handleSetOrderInWork = async () => {
const orderId = props.order.id;
const serverRes = await OrderService.setOrderInWork(orderId);
console.log(serverRes);
navigate(`/orders/${orderId}`);
}
return (
<div>
<div>
{/*<Link to={`/orders/${props.order.id}`} >{props.order.name}</Link>*/}
<MyButton onClick={handleSetOrderInWork}> {props.order.name} </MyButton>
<MyButton onClick={() => navigate('/start')}>MAIN</MyButton>
</div>
<div>
</div>
</div>
)
}
In the useEffect
of OrderById
, add params.id
as a dependency. That way the component should rerender when the URL changes. Like this:
useEffect(() => {
getOrderById(params.id);
// store.checkOrdersInWork();
}, [params.id])