React components don't re render when the state is changed

29 Views Asked by At

App.tsx:

import Router from "./components/Router.tsx"
import {useState} from "react"
import Car from "./components/Car.tsx"

export default function App() {
    const [items, setItems] = useState<Car[]>([])

    function addItem(newItem: Car) {
        setItems([...items, newItem])
    }

    const props = {
        items: items,
        setItems: setItems,
        addItem: addItem
    }

    return (
        <>
            <Router props={props}/>
        </>
    )
}

Router.tsx:

import {BrowserRouter, Routes, Route} from "react-router-dom";
import MainPage from "../pages/MainPage.tsx";
import AddPage from "../pages/AddPage.tsx";
import ReadPage from "../pages/ReadPage.tsx";
import UpdatePage from "../pages/UpdatePage.tsx";
import Car from "../components/Car.tsx"
import {Dispatch, SetStateAction} from "react"

interface Props {
    items: Car[],
    setItems: Dispatch<SetStateAction<Car[]>>;
    addItem: (newItem: Car) => void;
}

export default function Router({props}: { props: Props }) {
    return (
        <>
            <BrowserRouter>
                <Routes>
                    <Route index element={<MainPage props={props}/>}></Route>
                    <Route path="/home" element={<MainPage props={props}/>}></Route>
                    <Route path="/add" element={<AddPage props={props}/>}></Route>
                    <Route path="/read" element={<ReadPage props={props}/>}></Route>
                    <Route path="/update" element={<UpdatePage props={props}/>}></Route>
                    <Route path="*" element={<MainPage props={props}/>}></Route>
                </Routes>
            </BrowserRouter>
        </>
    )
}

AddPage.tsx:

import AddForm from "../components/AddForm.tsx"
import Car from "../components/Car.tsx"
import {Dispatch, SetStateAction} from "react"

interface Props {
    items: Car[];
    setItems: Dispatch<SetStateAction<Car[]>>;
    addItem: (newItem: Car) => void;
}

export default function AddPage({props}: { props: Props }) {
    return (
        <>
            <AddForm props={props}/>
        </>
    )
}

MainPage.tsx:

import List from "../components/List.tsx";
import Car from "../components/Car.tsx";
import {Dispatch, SetStateAction} from "react";

interface Props {
    items: Car[];
    setItems: Dispatch<SetStateAction<Car[]>>;
    addItem: (newItem: Car) => void;
}

export default function MainPage({props}: { props: Props }) {
    const handleAddClick = () => {
        window.open("/add", "_blank");
    }

    const handleReadClick = () => {
        window.open("/read", "_blank");
    }

    const handleUpdateClick = () => {
        window.open("/update", "_blank");
    }

    return (
        <>
            <button className="border rounded" onClick={handleAddClick}>add</button>
            <List props={props} onReadClick={handleReadClick} onUpdateClick={handleUpdateClick}
            />
        </>
    )
}

List.tsx:

import Car from "./Car.tsx";
import {Dispatch, SetStateAction} from "react";

interface Props {
    items: Car[];
    setItems: Dispatch<SetStateAction<Car[]>>;
    addItem: (newItem: Car) => void;
}

export default function List({props, onReadClick, onUpdateClick}: { props: Props, onReadClick: () => void, onUpdateClick: () => void }) {
    const items = props.items;
    // TODO update the list with the new item added
    const handleRead = () => {
        onReadClick();
    }

    const handleUpdate = () => {
        onUpdateClick();
    }

    const handleDelete = () => {
        // TODO
    }

    return (
        <>
            <ul className="list-group">
                {items.map((item, index) =>
                    <li className="list-group-item border rounded" key={index}>
                        {item.model}
                        <button className="border rounded" onClick={handleRead}>read</button>
                        <button className="border rounded" onClick={handleUpdate}>update</button>
                        <button className="border rounded" onClick={handleDelete}>delete</button>
                    </li>)
                }
            </ul>
        </>
    )
}

AddForm.tsx:

import Car from "./Car.tsx";
import {ChangeEvent, Dispatch, SetStateAction, useState} from "react"

interface Props {
    items: Car[];
    setItems: Dispatch<SetStateAction<Car[]>>;
    addItem: (newItem: Car) => void;
}

export default function AddForm({props}: { props: Props }) {
    const [formData, setFormData] = useState({brand: "", model: "", year: 0})
    const addItem = props.addItem;

    const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
        const changedField = event.target.name;
        const newValue = event.target.value;
        setFormData(currentData => {
            return {
                ...currentData,
                [changedField]: newValue
            }
        })
    }

    const handleAdd = () => {
        addItem(new Car(formData.brand, formData.model, formData.year));
    }

    return (
        <form className="position-absolute top-50 start-50 translate-middle">
            <input id="input-car-brand" name="brand" placeholder="brand" onChange={handleInputChange}/>
            <input id="input-car-model" name="model" placeholder="model" onChange={handleInputChange}/>
            <input id="input-car-year" name="year" placeholder="year" onChange={handleInputChange}/>
            <button className="border rounded" type="button" onClick={handleAdd}>add
            </button>
        </form>
    );
}`

The app will render the MainPage first, where I have a button "add", which when it is pressed it will open the AddPage in a new tab with a form to input values for a new object Car. When I press "add" in the AddPage, it won't cause a re render on the MainPage to display the new list of items.

I tried using Context but still didn't work.

0

There are 0 best solutions below