繁体   English   中英

如何在功能组件之间传递状态?

[英]How do I pass state between functional components?

我目前正在使用带有react-hooks的react-js编写注册页面,但我仍在学习,因此,如果这是一个非常简单的问题,请原谅。

我有一个用钩子在功能组件中编写的signup.js。 signup.js导入了'EmailTextField','PasswordTextField','NameTextField','CellPhoneTextField'...其中的组件也使用钩子编写在功能组件中。

我将所有这些文本字段都作为单独的组件进行了简化,以简化代码,因为我需要对每个文本字段进行许多不同的检查。 (并且在signup.js页面中包含所有这些字段会产生很长的代码)

在signup.js的过程结束时,我想获取其所有子组件(所有这些文本字段)状态(无论用户是否登录的状态)的状态,但是我不确定如何通过从这些文本字段到signup.js的状态(或变量)。

我知道redux可以管理状态,但是没有redux还是可以实现这一点的吗?

谢谢。

我创建了一个CodeSandbox示例,其中包含最少的示例代码。

在这里,我在apptest.js使用EmailTextfield组件。 我想从apptest.js获取EmailTextfield上的isValid状态,以便我可以确保在用户注册之前验证所有字段。

'./components/UI/Textfield/EmailTextField.js'

import React, { useState } from "react";
import TextField from "@material-ui/core/TextField";
import Grid from "@material-ui/core/Grid";

export const EmailTextField = props => {
  const [value, setValue] = useState("");
  const [helperText, setHelperText] = useState(
    "Email address will be used as your username."
  );
  const [isValid, setIsValid] = useState("true");

  const handleOnChangeEmailAddress = event => {
    // Email Validation logic
    if (true) {
      setIsValid(true);
    } else {
      setIsValid(false);
    }
  };

  return (
    <Grid item xs={12}>
      <TextField
        variant="outlined"
        required
        fullWidth
        id="email"
        label="email address"
        error={!isValid}
        helperText={helperText}
        name="email"
        autoComplete="email"
        margin="dense"
        onBlur={handleOnChangeEmailAddress}
      />
    </Grid>
  );
};

export default EmailTextField;

'aptest.js'

import React from "react";
import CssBaseline from "@material-ui/core/CssBaseline";
import Grid from "@material-ui/core/Grid";
import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container";
import { EmailTextField } from "./components/UI/Textfield/EmailTextField";

const useStyles = makeStyles(theme => ({
  "@global": {
    body: {
      backgroundColor: theme.palette.common.white
    }
  },
  paper: {
    marginTop: theme.spacing(8),
    display: "flex",
    flexDirection: "column",
    alignItems: "center"
  },
  mainBox: {
    // margin: '200px',
    width: "550px",
    textAlign: "left",
    boxShadow: "0 2px 3px #ccc",
    border: "1px solid #eee",
    padding: "40px 70px 50px 70px",
    boxSizing: "border-box"
  },
  form: {
    width: "100%", // Fix IE 11 issue.
    marginTop: theme.spacing(3)
  }
}));

const Apptest = props => {
  const classes = useStyles();
  return (
    <Container component="main" maxWidth="xs">
      <CssBaseline />
      <div className={classes.paper}>
        <div className={classes.mainBox}>
          <form className={classes.form} noValidate>
            <Grid container spacing={2}>
              <EmailTextField />
            </Grid>
          </form>
        </div>
      </div>
    </Container>
  );
};

export default Apptest;

我的想法很粗略。

输入字段周围应该有一个一致的数据模型。 该数据模型应该是该特定输入字段的唯一事实来源。 它应该能够判断该特定字段是否被触摸,是否有错误,是否原始,它的价值和所有其他东西。

因此,假设您拥有这样的服务:

errors: [],
onChange: false,
pristine: true,
touched: false,
value,

我们称之为StateChangeEvent

现在,每个Input字段都将具有处理事件的处理程序,例如更改和模糊。 在这里,单个组件将更新StateChangeEvent。 这些方法最终将使用StateChangeEvent作为参数调用回调函数。

这样,父母将知道其中一个字段发生了变化,并且它可以做出相应的响应。

在父组件中,要启用表单上的“提交按钮”,我们还可能会有副作用,它将更新表单的整体状态。 像这样:

useEffect(() => {
  const isValid = !fieldOne.onChange &&
    fieldOne.errors.length === 0 &&
    fieldOne.value.length !== 0 &&
    !fieldTwo.onChange &&
    fieldTwo.errors.length === 0 &&
    fieldTwo.value.length !== 0 &&
    ...;
  setIsFormValid(isValid);
}, [fieldOne, fieldTwo, ...]);

我确定这不是一个完整的解决方案。 但我敢肯定,它将帮助您入门。

更新:

根据您提供的CodeSandbox,可以执行以下操作:

import ...

const useStyles = makeStyles(theme => ({ ... }));

const Apptest = props => {

  const classes = useStyles();
  const [isInvalid, setIsInvalid] = useState(true);

  const handleStateChange = updatedState => {
    console.log("updatedState: ", updatedState);
    updatedState.errors.length === 0 ? setIsInvalid(false) : setIsInvalid(true);
  };

  return (
    <Container component="main" maxWidth="xs">
      <CssBaseline />
      <div className={classes.paper}>
        <div className={classes.mainBox}>
          <form className={classes.form} noValidate>
            <Grid container spacing={2}>
              <EmailTextField onStateChange={handleStateChange} />
            </Grid>
            <Button
              variant="contained"
              color="primary"
              disabled={isInvalid}
              className={classes.button}
            >
              Submit
            </Button>
          </form>
        </div>
      </div>
    </Container>
  );
};

export default Apptest;

EmailTextField组件中:

import React, { useState } from "react";
import TextField from "@material-ui/core/TextField";
import Grid from "@material-ui/core/Grid";

export const EmailTextField = props => {
  const { onStateChange } = props;
  const [state, setState] = useState({
    errors: [],
    onChange: false,
    pristine: true,
    touched: false,
    value: null
  });
  const helperText = "Email address will be used as your username.";

  const handleBlur = event => {
    // Email Validation logic
    const matches = event.target.value.match(
      `[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,3}`
    );
    if (matches) {
      const updatedState = {
        ...state,
        touched: true,
        value: event.target.value,
        errors: []
      };
      setState(updatedState);
      onStateChange(updatedState);
    } else {
      const updatedState = {
        ...state,
        touched: true,
        value: event.target.value,
        errors: ["Please enter a valid email"]
      };
      setState(updatedState);
      onStateChange(updatedState);
    }
  };

  return (
    <Grid item xs={12}>
      <TextField
        variant="outlined"
        required
        fullWidth
        id="email"
        label="email address"
        error={state.errors.length > 0}
        helperText={state.errors.length > 0 ? state.errors[0] : helperText}
        name="email"
        autoComplete="email"
        margin="dense"
        onBlur={handleBlur}
      />
    </Grid>
  );
};

export default EmailTextField;

这是供您参考的工作CodeSandbox示例

我想通了,很抱歉收到您的延迟回复。 我睡着了。基本上是onBlur()进行回调,现在在这种情况下,您需要将输入框中的值传递给回调,以便可以访问用户输入的值。 另一种方法是使用onChange()来跟踪更改并进行设置,以便在调用onblur时可以检查该value ,然后可以执行验证。

因此,您只需将事件的target传递给callback例如onBlur={(e) => handleOnChangeEmailAddress(e.target.value)} ,然后就可以访问method中的值。 我已经重构了您在沙箱中共享的代码。 在下面找到我所做的摘要。

import React, { useState } from "react";
import TextField from "@material-ui/core/TextField";
import Grid from "@material-ui/core/Grid";

export const EmailTextField = props => {
  const [value, setValue] = useState("");
  const [helperText, setHelperText] = useState(
    "Email address will be used as your username."
  );
  const [isValid, setIsValid] = useState("true");

  const handleOnChangeEmailAddress = value => {
    // Email Validation logic
    if (!value) {
      setIsValid(true);
    } else {
      setIsValid(false);
    }
    console.log(isValid)
  };

  return (
    <Grid item xs={12}>
      <TextField
        variant="outlined"
        required
        fullWidth
        id="email"
        label="email address"
        error={!isValid}
        helperText={helperText}
        name="email"
        autoComplete="email"
        margin="dense"
        onBlur={(e) => handleOnChangeEmailAddress(e.target.value)}
      />
    </Grid>
  );
};

export default EmailTextField;

希望对您有帮助。.如果您有任何问题,请随时提出问题。

从您的codesandbox示例中,您似乎几乎就在那儿,您只需要传递onStateChange函数作为道具:

<EmailTextField onStateChange={onStateChange} />

然后在您的apptest.js文件中实现onStateChange函数,该函数将获取更新的对象。

在下面查看我的示例并打开控制台,如果电子邮件有效,您将看到控制台日志中的错误和“ isValid”响应。

https://codesandbox.io/s/loving-blackwell-nylpy?fontsize=14

暂无
暂无

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

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