如何将输入值从子表单组件传递到其父组件的状态以使用反应钩子提交?

Posted

技术标签:

【中文标题】如何将输入值从子表单组件传递到其父组件的状态以使用反应钩子提交?【英文标题】:How to pass input values from a child Form component to the state of its parent component for submission with react hooks? 【发布时间】:2020-02-15 20:04:31 【问题描述】:

我正在设置一个多步骤表单来处理使用 Next.js 和 Material-UI 进行的登录。在位于pages/signup.jssignup.js 页面中。我的多步骤表单已创建。在该页面中,作为步骤内容显示的子组件在getStepContent(step) 中定义,主组件在其下方称为SignInSide。前进和后退的步骤也在signup.js 页面上定义。子表单组件仅包含输入。我正在尝试将输入到子组件表单输入中的数据传递给注册页面中父组件的状态signup.js,以便我可以提交它。父组件和子组件都是函数式组件,使用 React hooks 处理状态。

我已经尝试将 props 从父组件传递到子组件,然后将 TextField 组件的“值”定义为 props.value 并且当我使用 console.log 中的 value 测试它时,它起作用了父组件我看到了字母,但是没有名称:附加在它上面,它只是作为字母通过所以我尝试添加 [name]: event.target.value 但是当我尝试编写文本时在输入字段中它只是说 [Object object] 但是,在控制台中我可以看到 [Object object](在此处键入的插入字母),因此它记录了文本但不正确并且键入的文本在输入框中不可见。

这是步骤内容所在。

const steps = ['Welcome', 'Information', 'Creative', 'Confirm'];

function getStepContent(step) 
  const [value, setValue] = React.useState('');

  //set state for returned input values
  function handleChange(newValue) 
    setValue(newValue);
  

  switch (step) 
    case 0:
      return (
        <div style= padding: '8em 0 4em' >
          <Hero title='Welcome' paragraph="Let's get you signed up." />
        </div>
      );
    case 1:  // Form Components
      return <CredentialsForm value=value onChange=handleChange />;
    case 2:
      return <CreativeForm />;
    case 3:
      return <Review />;
    default:
      throw new Error('Unknown step');
  


// Sign In Page
export default function SignInSide() 
  const classes = useStyles();
  const [activeStep, setActiveStep] = React.useState(0);
  const [form, setForm] = React.useState(false);
  // Forward and Back button functions, the buttons are defined here, in the parent page component.
  const handleNext = () => 
    setActiveStep(activeStep + 1);
  ;

  const handleBack = () => 
    setActiveStep(activeStep - 1);
  ;

  const handleFormExtend = () => 
    setForm(true);
    setActiveStep(activeStep + 1);
  ;

  return (
    ...
  )
 

父组件是SignInSide。我正在使用材料-ui。可以在此处看到我使用的步进器表单的示例:https://material-ui.com/getting-started/templates/checkout/ 它的类似代码,我只是将文本字段更改为概述并设置了从父级传递的道具的逻辑。

&lt;CredentialsForm value=value onChange=handleChange /&gt; 组件内部。

export default function CredentialsForm(props) 
  const classes = useStyles();
  // Floating Form Label
  const inputLabel = React.useRef(null);
  const [labelWidth, setLabelWidth] = React.useState(0);
  React.useEffect(() => 
    setLabelWidth(inputLabel.current.offsetWidth);
  , []);

  //Send input value to parent state
  const handleChange = name => event => 
    props.onChange(
      [name]: event.target.value
    );
  ;

  return (
    <div className=classes.paper>
      <Avatar className=classes.avatar>
        <LockOutlinedIcon />
      </Avatar>
      <Typography component='h1' variant='h5'>
        Who Are U?
      </Typography>
      <form className=classes.form noValidate>
        <Grid container spacing=2>
          <Grid item xs=2 md=2>
            <FormControl
              margin='normal'
              variant='outlined'
              fullWidth
              required
              className=classes.formControl
            >
              <InputLabel
                ref=inputLabel
                htmlFor='outlined-userTitle-native-simple'
              >
                Title
              </InputLabel>
              <Select
                native
                value=props.value
                onChange=handleChange('title')
                labelWidth=labelWidth
                required
                inputProps=
                  name: 'title'
                
              >
                <option value='' />
                <option value=10>Mr.</option>
                <option value=20>Ms.</option>
                <option value=20>Mrs.</option>
                <option value=30>Non-Binary</option>
              </Select>
            </FormControl>
          </Grid>
          <Grid item xs=5 md=5>
            <TextField
              variant='outlined'
              margin='normal'
              required
              fullWidth
              value=props.value
              onChange=handleChange('firstName')
              id='first'
              label='First Name'
              name='firstname'
            />
          </Grid>
        </Grid>
      </form>
    </div>
  );


预期:在用户输入时,子组件将输入保存为状态并将其传递给父组件,以便它可以提交数据以进行注册。

实际:输入已成功传递给父级,但格式错误。它作为“[Object object](用户在此处键入的字母)”传递,并且在输入字段中,用户只能看到控制台中看到的 [Object object]。没有错误。

【问题讨论】:

【参考方案1】:

你的问题在这里:

const handleChange = name => event => 
  props.onChange(
    [name]: event.target.value
  );
;

您正在为您的props.onChange 函数提供一个对象,但该函数正在更新一个使用字符串值初始化的状态挂钩。然后你将文本值设置为一个对象,它曾经是一个字符串,给你看起来很奇怪的对象文本。

此外,您将所有表单值设置为与父状态相同的字符串。

要解决此问题,请将您的状态设置为对象而不是字符串。

在父母中:

const [values, setValues] = React.useState(title: '', firstName: '');

....

function handleChange(newValue) 
  setValues(...values, ...newValue);


....

return <CredentialsForm values=values onChange=handleChange />;

在孩子中:

<Select
  native
  value=props.values.title // Change to this
  onChange=handleChange('title')
  labelWidth=labelWidth
  required
  inputProps=
    name: 'title'
  
>

....

<TextField
  variant='outlined'
  margin='normal'
  required
  fullWidth
  value=props.values.firstName // Change to this
  onChange=handleChange('firstName')
  id='first'
  label='First Name'
  name='firstname'
/>

推荐但不是必需的:我会去掉表单组件中的handleChange 函数,因为它并没有真正增加价值。然后直接从输入调用props.onChange,并将你的父handleChange更改为:

const handleChange = name => event => 
  setValues(...values, [name]: event.target.value);

【讨论】:

哇!我完全忽略了状态被设置为父级中的字符串!非常感谢你!

以上是关于如何将输入值从子表单组件传递到其父组件的状态以使用反应钩子提交?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用从子组件传递给组件的反应变量更新 redux 状态

我如何将 useState 值从子级传递给父级

如何使用双向数据绑定将数据从子组件传递到父组件?

将一个变量的值从子组件传递给两个父组件,vuejs? nuxt

如何在Angular 4中将值从子组件传递到父组件

React Hook:将数据从子组件发送到父组件