Search code examples
javascriptreactjsreact-router-dom

React (18.2.0) changes URL using navigate, but the page will not update


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>
    )
}



Solution

  • 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])