React 控制的 MUI 表单字段组件

React controlled MUI form field components

我觉得我错过了明显的东西,但我似乎找不到另一个 post 来解决我遇到的问题。我试图让最终用户能够为对象设置位置,但可以将信息输入表单或单击地图上的位置。我有一个包含三个组件的反应页面。

  1. 包含其他两个组件的父容器组件。
import {useState} from 'react';
import Box from '@mui/material/Box';

import ObjSetLocationForm from './ObjSetLocationForm';
import ObjSetLocationMap from './ObjSetLocationMap';

export default function LoadSetLocationSet( props ) {
    const [prevLat, setPrevLat] = useState("43.5666694");
    const [prevLng, setPrevLng] = useState("-101.0716746");
    const [newLat, setNewLat] = useState();
    const [newLng, setNewLng] = useState();

    function setCoords(coords){
        setNewLat(coords.lat);
        setNewLng(coords.lng);
    }


    return(
        <Box>
            <ObjSetLocationForm prevLat={prevLat} prevLng={prevLng} newLat={newLat} newLng={newLng} setCoordsFx={setCoords} />
            <ObjSetLocationMap prevLat={prevLat} prevLng={prevLng} newLat={newLat} newLng={newLng} setCoordsFx={setCoords} />
        </Box>
    );
}
  1. 在地图 A 上显示单个点并允许最终用户单击地图以将点设置到新位置 (lat/lng) 的地图组件,并且是父容器的子项.我不会 post 这个组件的所有代码,因为它相当大,但重要的部分是当用户点击地图时,新的 lat/lng 值通过调用传递回父组件setCoords 函数就像这样。
        props.setCoordsFx({lat: e.lngLat.lat, lng: e.lngLat.lng});

我已经验证它工作正常并且正确地传递了值。

  1. 一个 Form 组件,它是父容器的第二个子组件,允许最终用户输入 Lat/Lng 来更改地图上单个点的位置。这个组件是我遇到问题的地方。我有两个 html 形式的 MUI TextField,我想在用户单击地图时将其设置为 lat/lng 值。当我 运行 通过调试器时,我可以看到值从父级传递给这个组件,我什至可以看到控制组件的状态值正在设置,但 TextFields 值永远不会改变。
import {useState, useEffect, useContext} from 'react';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';


export default function LoadSetLocationSet( props ) {
    const [newLat, setNewLat] = useState(props.newLat);
    const [newLng, setNewLng] = useState(props.newLng);
  

    function handleSubmit(e) {
        e.preventDefault();

        // Save values to DB.
    } 

    return(
        <Box>
            <form id="SelLocationForm" onSubmit={handleSubmit}>
                <Box sx={{textAlign:'center'}}>
                    <Typography variant="h6" sx={{display:'inline'}}>Current Location</Typography>
                    <Box sx={{display:'flex', alignItems:'center', justifyContent:'center'}}>
                        <Stack direction="row" spacing={3}>
                            <Box>
                                <Box>
                                    <Typography>Latitude</Typography>
                                </Box>
                                <Box>
                                    <Typography>{props.prevLat}</Typography>
                                </Box>
                            </Box>
                            <Box>
                                <Box>
                                    <Typography>Longitude</Typography>
                                </Box>
                                <Box>
                                    <Typography>{props.prevLng}</Typography>
                                </Box>
                            </Box>
                        </Stack>
                    </Box>
                </Box>


                <Box sx={{textAlign:'center', pt:2}}>
                    <Typography variant="h6" sx={{mb:2}}>Enter the Latitude and Longitude or click the new location on the map</Typography>
                    <Typography variant="h6" sx={{display:'inline'}}>New Location</Typography>
                    <Box sx={{display:'flex', alignItems:'center', justifyContent:'center'}}>
                        <Stack direction="row" spacing={3}>
                            <Box>
                                <TextField
                                    id="tbLatitude"
                                    label="Latitude"
                                    type="text"
                                    size="small"
                                    value={newLat}
                                    onChange={(e) => {setNewLat(e.target.value);}}
                                />
                            </Box>
                            <Box>
                                <TextField
                                    id="tbLongitude"
                                    label="Longitude"
                                    type="text"
                                    size="small"
                                    value={newLng}
                                    onChange={(e) => {setNewLng(e.target.value);}}
                                />
                            </Box>
                            <Box sx={{display:'flex', alignItems:'center', justifyItems:'center'}}>
                                <Button variant="contained" type="submit">Set</Button>
                            </Box>
                        </Stack>
                    </Box>
                </Box>
            </form>
        </Box>
    );
}

如您所见,我正在尝试使用受控的 TextField。所以这是我的 questions/problems:

  1. 如果将默认值设置为 prop 值是“反模式”,如果表单字段是受控表单字段,我应该如何设置默认值?

  2. 正如我之前所说,当用户单击地图上的某个位置时,它应该刷新表单子组件并将我控制的表单字段的值设置为传入的值,但这不起作用.我怎样才能做到这一点?

我以为我理解了一些事情,因为我现在已经做了一些反应,但我似乎迷路了。抱歉新手问题。

在您的 LoadSetLocationSet 中,您将 newLatnewLng 作为道具传递。你还应该传入 setNewLatsetNewLng 作为道具。

问题是你在两个地方定义了newLatnewLng:

export default function LoadSetLocationSet( props ) {
    const [prevLat, setPrevLat] = useState("43.5666694");
    const [prevLng, setPrevLng] = useState("-101.0716746");
    const [newLat, setNewLat] = useState();
    const [newLng, setNewLng] = useState();

export default function LoadSetLocationSet( props ) {
    const [newLat, setNewLat] = useState(props.newLat);
    const [newLng, setNewLng] = useState(props.newLng);

这会导致问题,因为您实际上只需要 该数据的一个真实来源,如果您试图跟踪该状态,事情就会变得混乱和混乱两个不同的地方。相反,一个地方跟踪newLatnewLng。在 React 中,数据流从父级向下流向子级,因此我们经常希望 lift state up

从子表单中删除这些行:

    const [newLat, setNewLat] = useState(props.newLat);
    const [newLng, setNewLng] = useState(props.newLng);

然后在父级中我们将把 setNewLatsetNewLng 作为 props 传递给表单:

            <ObjSetLocationForm prevLat={prevLat} prevLng={prevLng} newLat={newLat} newLng={newLng} setCoordsFx={setCoords} setNewLat={setNewLat} setNewLng={setNewLng} />
            <ObjSetLocationMap prevLat={prevLat} prevLng={prevLng} newLat={newLat} newLng={newLng} setCoordsFx={setCoords} />

然后您只需调整 TextFields 中的 onChange 函数即可使用作为 props 传入的 setNewLatsetNewLng 函数。您还需要更改 value 以使用从 props 传递的数据,以便设置这些字段的 default/initial 值:

                                <TextField
                                    id="tbLatitude"
                                    label="Latitude"
                                    type="text"
                                    size="small"
                                    value={props.newLat}
                                    onChange={(e) => {props.setNewLat(e.target.value);}}
                                />
                            </Box>
                            <Box>
                                <TextField
                                    id="tbLongitude"
                                    label="Longitude"
                                    type="text"
                                    size="small"
                                    value={props.newLng}
                                    onChange={(e) => {props.setNewLng(e.target.value);}}
                                />

主要思想是只在一个地方跟踪数据,通常是父组件。即使我们将数据存储在父级中,我们仍然可以通过将 callback 作为道具传递给子级来更新子级的状态。