简体   繁体   English

React 控制的 MUI 表单字段组件

[英]React controlled MUI form field components

I feel like I am missing the obvious but I can't seem to find another posts that addresses the issue I am having.我觉得我错过了明显的东西,但我似乎找不到其他帖子来解决我遇到的问题。 I am trying to allow an end user the ability to set a location for an object but either entering the information into a form or by clicking a location on a map. I have a react page with three components.我试图让最终用户能够为 object 设置位置,但要么将信息输入表单,要么通过单击 map 上的位置。我有一个包含三个组件的反应页面。

  1. A Parent container component that holds the other two components.包含其他两个组件的父容器组件。
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 Map component that shows a single point on a map A and allows the end user to click on the map to set the point to a new location (lat/lng) and is a child of the Parent container. Map 组件显示 map A 上的单个点并允许最终用户单击 map 以将该点设置为新位置 (lat/lng),并且是 Parent 容器的子项。 I will not post all the code to this component because it is pretty big but the important part is that when a user clicks on the map the new lat/lng values are passed back up to the parent component by calling the setCoords function like so.我不会将所有代码都发布到该组件,因为它非常大,但重要的部分是当用户单击 map 时,新的 lat/lng 值将通过调用 setCoords function 传递回父组件,就像这样。
        props.setCoordsFx({lat: e.lngLat.lat, lng: e.lngLat.lng});

I have verified that this is working correctly and is passing the values correctly.我已经验证它工作正常并且正确传递了值。

  1. A Form component that is the second child of the parent container and allows the end user to enter a Lat/Lng to change the position of the single point on the map. This component is where I am having issues.一个 Form 组件,它是父容器的第二个子组件,允许最终用户输入 Lat/Lng 以更改 map 上单点的 position。这个组件是我遇到问题的地方。 I have two MUI TextFields in an html form that I want to set to the lat/lng values when the user clicks on the map. When I run through the debugger I can see the values getting passed down to this component from the parent and I can even see that the state values that control the components are getting set but the TextFields values never change.我有两个 html 形式的 MUI 文本字段,当用户单击 map 时,我想将其设置为纬度/经度值。当我运行调试器时,我可以看到值从父项传递到此组件,我甚至可以看到控制组件的 state 值正在设置,但 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>
    );
}

As you can see I am attempting to use controlled TextFields.如您所见,我正在尝试使用受控的 TextFields。 So here are my questions/problems:所以这是我的问题/问题:

  1. If setting the default value to a prop value is "anti pattern" how am I supposed to set the default value for form fields if they are a controlled form field?如果将默认值设置为道具值是“反模式”,如果它们是受控表单字段,我应该如何设置表单字段的默认值?

  2. As I stated earlier when the user clicks on a location on the map it should refresh the form child component and set the values for my controlled form fields to the values passed in but this is not working.正如我之前所说,当用户单击 map 上的某个位置时,它应该刷新表单子组件并将我控制的表单字段的值设置为传入的值,但这是行不通的。 How can I accomplish this?我怎样才能做到这一点?

I thought I understood things as I have been doing react for a little bit now but I seem to be lost.我以为我已经理解了一些事情,因为我现在已经做了一些反应,但我似乎迷路了。 Sorry for the newbie question.对不起,新手问题。

In your LoadSetLocationSet you are passing in newLat and newLng as props.在您的LoadSetLocationSet中,您将newLatnewLng作为道具传递。 You should also pass in setNewLat and setNewLng as props.您还应该将setNewLatsetNewLng作为道具传递。

The problem is that you are defining newLat and newLng in two places :问题是您在两个地方定义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();

and

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

That is going to cause issues since you really only want one source of truth for that data, and things get messy and confusing if you try to keep track of that state in two separate places.这会导致问题,因为您实际上只需要该数据的一个真实来源,如果您试图在两个不同的地方跟踪 state,事情就会变得混乱和混乱。 Instead, only keep track of newLat and newLng in a single place .相反,一个地方跟踪newLatnewLng In React, the flow of data goes downward from parents to children, so we often want to lift state up在 React 中,数据的流向是从父级向下流向子级,所以我们经常想将 state 向上提升

Remove these lines from the child form:从子表单中删除这些行:

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

Then in the parent we will pass down setNewLat and setNewLng as props to the Form:然后在父级中,我们将setNewLatsetNewLng作为 props 传递给 Form:

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

Then you just have to adjust the onChange functions in your TextFields to use the setNewLat and setNewLng functions that are passed in as props.然后,您只需调整TextFields中的onChange函数即可使用作为道具传入的setNewLatsetNewLng函数。 You will also need to change value to use the data passed from props in order to set the default/initial value of those fields:您还需要更改value以使用从 props 传递的数据,以便设置这些字段的默认/初始值:

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

The main idea is to keep track of data in only one place , often a parent component.主要思想是只在一个地方跟踪数据,通常是父组件。 Even though we store data in the parent, we can still update state from a child by passing down a callback to the child as a prop.即使我们将数据存储在父级中,我们仍然可以通过将回调作为道具传递给子级来从子级更新 state。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM