如何在功能组件之间传递状态?
How do I pass state between functional components?
我目前正在用 react-js 和 react-hooks 编写一个注册页面,我还在学习,所以如果这是一个非常简单的问题,请原谅。
我有一个 signup.js 用带钩子的功能组件编写的。 signup.js imports 'EmailTextField', 'PasswordTextField', 'NameTextField', 'CellPhoneTextField' ...的组件也写在带有钩子的功能组件中。
我将所有这些文本字段作为单独的组件来简化代码,因为我需要对每个文本字段进行许多不同的检查。 (并且在 signup.js 页面中包含所有这些字段会产生很长的代码)
在 signup.js 中的流程结束时,我想了解其所有子组件(所有这些文本字段)的状态(用户是否愿意登录。)但是我不确定如何将状态(或变量)从这些文本字段传递到 signup.js。
我知道 redux 可以管理状态,但是没有 redux 是否可以实现这个?
谢谢。
我创建了一个 CodeSandbox Sample,其中包含最少的示例代码。
在这里,我在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;
我有一个非常粗略的实现。
输入字段周围应该有一致的 data-model。该数据模型应该是该特定输入字段的单一真实来源。它应该能够判断该特定字段是否被触及、是否有错误、是否是原始的、它的价值是什么以及所有这些东西。
所以假设你是这样的:
errors: [],
onChange: false,
pristine: true,
touched: false,
value,
我们称它为 StateChangeEvent
。
现在每个输入字段都会有一个事件处理程序,例如更改和模糊。此处,单个组件将更新 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;
Here's a Working CodeSandbox Sample for your ref.
我明白了,抱歉回复晚了。我是 asleep.Basically 一个 onBlur()
接受回调,现在在这种情况下,您需要将输入框中的值传递给回调,以便您可以访问用户输入的值。另一种方法是使用 onChange()
来跟踪更改并设置它,以便在调用 onblur
时您可以检查 value
然后您可以执行验证。
所以你只需要像这样 onBlur={(e) => handleOnChangeEmailAddress(e.target.value)}
将事件的 target
值传递给 callback
,然后你就可以访问方法中的值。我已经重构了您在沙盒中共享的代码。在下面找到我所做的片段。
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
函数作为 prop 传递:
<EmailTextField onStateChange={onStateChange} />
然后在您的 apptest.js
文件中实现 onStateChange
函数,这将获取更新后的对象。
查看我下面的示例并打开控制台,您将看到错误的控制台日志和 "isValid" 响应(如果电子邮件有效)。
我目前正在用 react-js 和 react-hooks 编写一个注册页面,我还在学习,所以如果这是一个非常简单的问题,请原谅。
我有一个 signup.js 用带钩子的功能组件编写的。 signup.js imports 'EmailTextField', 'PasswordTextField', 'NameTextField', 'CellPhoneTextField' ...的组件也写在带有钩子的功能组件中。
我将所有这些文本字段作为单独的组件来简化代码,因为我需要对每个文本字段进行许多不同的检查。 (并且在 signup.js 页面中包含所有这些字段会产生很长的代码)
在 signup.js 中的流程结束时,我想了解其所有子组件(所有这些文本字段)的状态(用户是否愿意登录。)但是我不确定如何将状态(或变量)从这些文本字段传递到 signup.js。
我知道 redux 可以管理状态,但是没有 redux 是否可以实现这个?
谢谢。
我创建了一个 CodeSandbox Sample,其中包含最少的示例代码。
在这里,我在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;
我有一个非常粗略的实现。
输入字段周围应该有一致的 data-model。该数据模型应该是该特定输入字段的单一真实来源。它应该能够判断该特定字段是否被触及、是否有错误、是否是原始的、它的价值是什么以及所有这些东西。
所以假设你是这样的:
errors: [],
onChange: false,
pristine: true,
touched: false,
value,
我们称它为 StateChangeEvent
。
现在每个输入字段都会有一个事件处理程序,例如更改和模糊。此处,单个组件将更新 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;
Here's a Working CodeSandbox Sample for your ref.
我明白了,抱歉回复晚了。我是 asleep.Basically 一个 onBlur()
接受回调,现在在这种情况下,您需要将输入框中的值传递给回调,以便您可以访问用户输入的值。另一种方法是使用 onChange()
来跟踪更改并设置它,以便在调用 onblur
时您可以检查 value
然后您可以执行验证。
所以你只需要像这样 onBlur={(e) => handleOnChangeEmailAddress(e.target.value)}
将事件的 target
值传递给 callback
,然后你就可以访问方法中的值。我已经重构了您在沙盒中共享的代码。在下面找到我所做的片段。
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
函数作为 prop 传递:
<EmailTextField onStateChange={onStateChange} />
然后在您的 apptest.js
文件中实现 onStateChange
函数,这将获取更新后的对象。
查看我下面的示例并打开控制台,您将看到错误的控制台日志和 "isValid" 响应(如果电子邮件有效)。