

# やりたいこと

Reactを使ったWebアプリのフロントエンド部分で、ユーザーがformに入力した値でstateを更新したい。

# 状況

ユーザーがformに入力してもsubmitを押してもstateが更新されない。  
`useState`を別のstateの中に入れたことが原因と推測していますが、解決策が分かりません。

# ディレクトリ構成

[code]

    front
    ├── node_modules
    │   └── ...
    ├── public
    │   └── ...
    ├── package-lock.json
    ├── package.json
    ├── tsconfig.json
    ├── src
    │   ├── components
    │   │   ├── Form.tsx
    │   │   └── Route.tsx
    │   ├── App.tsx
    │   ├── consts.ts
    │   ├── index.tsx
    │   ├── types.tsx
    │   └── ...
    └── ...
    
[/code]

# コード

`App.tsx`

[code]

    import { useState } from "react"
    import Form from "./components/Form";
    import { TimeSpecType, ViaListType, RouteType, QueryType, modesType } from "./types";
    import { NUM_ROUTES } from "./consts";
    
    function App() {
        const [viaCount, setViaCount] = useState<number>(0);
    
        const [index, setIndex] = useState<number | null>(null);
        const [origin, setOrigin] = useState<string>("");
        const [destination, setDestination] = useState<string>("");
        const [datetime, setDatetime] = useState<Date>(new Date());
        const [timeSpec, setTimeSpec] = useState<TimeSpecType>("departure");
        const [vias, setVias] = useState<ViaListType>(["", "", ""]);
    
        const route: RouteType = {
            index: index,
            setIndex: setIndex,
            origin: origin,
            setOrigin: setOrigin,
            destination: destination,
            setDestination: setDestination,
            datetime: datetime,
            setDatetime: setDatetime,
            timeSpec: timeSpec,
            setTimeSpec: setTimeSpec,
            vias: vias,
            setVias: setVias,
        };
    
        const [routes, setRoutes] = useState<RouteType[]>(new Array<RouteType>(NUM_ROUTES).fill(route));
    
        const [modes, setModes] = useState<modesType>({
            al: true,
            shin: true,
            ex: true,
            hb: true,
            lb: true,
            sr: true,
        });
    
        const [speed, setSpeed] = useState<number | null>(null);
        const [order, setOrder] = useState<number | null>(null);
        const options = {
            modes: modes,
            setModes: setModes,
            speed: speed,
            setSpeed: setSpeed,
            order: order,
            setOrder: setOrder,
        };
    
        const query: QueryType = {
            viaCount: viaCount,
            setViaCount: setViaCount,
            routes: routes,
            setRoutes: setRoutes,
            options: options,
        };
    
        const [routeCount, setRouteCount] = useState<number>(1);
    
        const handleSubmit= (e: React.FormEvent<HTMLFormElement>) => {
            e.preventDefault();
            console.log(e.target);  // Test
            console.log(routeCount, query);  // Test
    
            const response = axios.post("http://127.0.0.1:5000/", query)
            .catch(err => console.log(err));
        };
    
        return (
            <div className="App">
                <Form query={query} routeCount={routeCount} setRouteCount={setRouteCount} handleSubmit={handleSubmit}/>
            </div>
        );
    }
    
    export default App;
    
    
[/code]

`Form.tsx`

[code]

    import { useState } from "react";
    import Route from "./Route";
    import Option from "./Option";
    import { QueryType } from "../types";
    import { NUM_ROUTES } from "../consts";
    
    
    type FormPropsType = {
        query: QueryType,
        routeCount: number, 
        setRouteCount: React.Dispatch<React.SetStateAction<number>>,
        handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void,
    }
    
    const Form = ({ query, routeCount, setRouteCount, handleSubmit }: FormPropsType) => {
        const [viaCount, setViaCount] = useState<number>(0);
    
        return (
            <form id="search" onSubmit={handleSubmit}>
                <ul className="routes">
                    {
                        Array.from({length: routeCount}, (_, index) => {
                            return (
                                <li key={index}>
                                    <Route viaCount={viaCount} setViaCount={setViaCount} index={index} route={query.routes[index]} handleSubmit={handleSubmit} />
                                </li>
                            );
                        })
                    }
                </ul>
    
                <button type="button" onClick={() => setRouteCount(prevCount => prevCount + 1)} disabled={routeCount >= NUM_ROUTES}>経路を追加</button>
    
                <button form="search" type="submit">検索</button>
            </form>
        );
    };
    
    export default Form;
    
    
[/code]

`Route.tsx`

[code]

    import Datetime from "react-datetime";
    import ja  from "moment";
    import { RoutePropsType, TimeSpecType } from "../types";
    
    type radioOptionType = {
        value: TimeSpecType,
        text: string,
    }
    
    const Route = ({ viaCount, setViaCount, index, route, handleSubmit }: RoutePropsType) => {
        const radioOptions: radioOptionType[] = [
            {value: "departure", text: "出発"}, 
            {value: "arrival", text: "到着"}, 
            {value: "first", text: "始発"}, 
            {value: "last", text: "終電"}, 
            {value: "none", text: "指定なし"}, 
        ];
    
        const toISOStringInJpTime = (datetime: Date): string => {
            let str: string = datetime.toLocaleString("ja", { timeZone: 'Asia/Tokyo' }).replaceAll("/", "-").replace(" ", "T");
            str = str.slice(0, 16);  // Remove seconds at the end
            return str
        };
        
        console.log(route);  // Test
    
        return (
            <div className="route">
                <input type="text" name="origin" placeholder="出発駅・バス停・施設" onChange={e => route.setOrigin(e.target.value)} />
                <input type="text" name="destination" placeholder="到着駅・バス停・施設" onChange={e => route.setDestination(e.target.value)} />
                <input type="datetime-local" name="datetime" value={toISOStringInJpTime(route.datetime)} onChange={e => route.setDatetime(new Date(e.target.value))} />
                
                <div className="radio-container">
                {radioOptions.map((option) => (
                        <label key={option.value}>
                            <input
                                type="radio"
                                value={option.value}
                                name={"time-spec-" + index}
                                onChange={() => route.setTimeSpec(option.value)}
                                checked={option.value === "departure"}
                            />
                            {option.text}
                        </label>
                    ))}
                </div>
            </div>
        );
    };
    
    export default Route;
    
    
[/code]

`types.ts`

[code]

    export type modesType = {
        al: boolean,
        shin: boolean,
        ex: boolean,
        hb: boolean,
        lb: boolean,
        sr: boolean,
    }
    
    export type FormType = {
        viaCount: number, 
        setViaCount: React.Dispatch<React.SetStateAction<number>>,
        query: QueryType,
    }
    
    export type TimeSpecType = "departure" | "arrival" | "first" | "last" | "none"
    
    export type ViaType = string | undefined
    
    export type ViaListType = ViaType[]
    
    export type ViaPropsType = {
        viaCount: number, 
        setViaCount: React.Dispatch<React.SetStateAction<number>>, 
        vias: ViaListType,
        setVias: React.Dispatch<React.SetStateAction<ViaListType>>
    }
    
    export type RouteType = {
        index: number | null,
        setIndex: React.Dispatch<React.SetStateAction<number | null>>
        origin: string,
        setOrigin: React.Dispatch<React.SetStateAction<string>>,
        destination: string,
        setDestination: React.Dispatch<React.SetStateAction<string>>,
        datetime: Date,
        setDatetime: React.Dispatch<React.SetStateAction<Date>>,
        timeSpec: TimeSpecType,
        setTimeSpec: React.Dispatch<React.SetStateAction<TimeSpecType>>,
        vias: ViaListType,
        setVias: React.Dispatch<React.SetStateAction<ViaListType>>,
    }
    
    export type optionsType = {
        modes: modesType,
        setModes: React.Dispatch<React.SetStateAction<modesType>>,
        speed: number | null,
        setSpeed: React.Dispatch<React.SetStateAction<number | null>>,
        order: number | null,
        setOrder: React.Dispatch<React.SetStateAction<number | null>>,
    }
    
    export type QueryType = {
        viaCount: number,
        setViaCount: React.Dispatch<React.SetStateAction<number>>,
        routes: RouteType[],
        setRoutes: React.Dispatch<React.SetStateAction<RouteType[]>>,
        options: optionsType,
    }
    
    export type FormPropsType = {
        viaCount: number, 
        setViaCount: React.Dispatch<React.SetStateAction<number>>,
        query: QueryType,
        handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void,
    }
    
    export type RoutePropsType ={
        viaCount: number
        setViaCount: React.Dispatch<React.SetStateAction<number>>,
        index: number | null,
        route: RouteType,
        handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void,
    }
    
    
[/code]

`consts.ts`

[code]

    export const NUM_ROUTES: number = 5;
    export const NUM_VIAS: number = 3;
    
[/code]

今回の質問に関係ないと思われる部分は省略しました。

# コンソールの表示

[code]

    {viaCount: 0, routes: Array(5), options: {…}, setViaCount: ƒ, setRoutes: ƒ}
    options
    : 
    {modes: {…}, speed: null, order: null, setModes: ƒ, setSpeed: ƒ, …}
    routes
    : 
    Array(5)
    0
    : 
    {index: null, origin: '', destination: '', setIndex: ƒ, setOrigin: ƒ, …}
    1
    : 
    {index: null, origin: '', destination: '', setIndex: ƒ, setOrigin: ƒ, …}
    2
    : 
    {index: null, origin: '', destination: '', setIndex: ƒ, setOrigin: ƒ, …}
    3
    : 
    {index: null, origin: '', destination: '', setIndex: ƒ, setOrigin: ƒ, …}
    4
    : 
    {index: null, origin: '', destination: '', setIndex: ƒ, setOrigin: ƒ, …}
    length
    : 
    5
    [[Prototype]]
    : 
    Array(0)
    setRoutes
    : 
    ƒ ()
    setViaCount
    : 
    ƒ ()
    viaCount
    : 
    0
    
[/code]

`query`の中の配列`routes`内の値（`origin`など）が更新されるよう意図していますが、されませんでした。

# その他

`npm`の`create-react-app`を使って雛形を作り、そこから上記コードを書きました。  
よろしくお願いいたします。


CC BY-SA 4.0
2023-10-20T14:29:39.053
96534
2023-10-20T14:29:39.053
38402
question
reactjs
Reactのstateが更新されない