Javascript 在React after多个异步useState设置程序中运行一些代码
我有一个功能性的React组件,在其中我使用Javascript 在React after多个异步useState设置程序中运行一些代码,javascript,reactjs,Javascript,Reactjs,我有一个功能性的React组件,在其中我使用useState来管理状态。通常,它只是一个包含两个字段的表单-code和env,用户可以手动填写并提交。但是,当组件加载时,我还想检查任何querystring参数,如果存在合适的参数,我想自动填充并提交表单。这样,用户就可以为特定的表单提交添加书签 我遇到的问题是,众所周知,useStatesetter是异步的,就像setState类内组件一样。由于这两个表单字段都由状态控制,设置这两个值将启动多个渲染,因此我应该将提交表单的代码放在哪里,以确保两
useState
来管理状态。通常,它只是一个包含两个字段的表单-code
和env
,用户可以手动填写并提交。但是,当组件加载时,我还想检查任何querystring参数,如果存在合适的参数,我想自动填充并提交表单。这样,用户就可以为特定的表单提交添加书签
我遇到的问题是,众所周知,useState
setter是异步的,就像setState
类内组件一样。由于这两个表单字段都由状态控制,设置这两个值将启动多个渲染,因此我应该将提交表单的代码放在哪里,以确保两个状态更新都已完成
表格如下:
下面是我所拥有的代码的简化、净化版本:
import React, { useState, useEffect } from "react";
import axios from "axios";
import queryString from "query-string";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import ToggleButtonGroup from "react-bootstrap/ToggleButtonGroup";
import ToggleButton from "react-bootstrap/ToggleButton";
import Card from "react-bootstrap/Card";
/*
* this component will show a spinner or the results from the API when complete
*/
const PortalDisplay = ({ data: portal, isLoading }) => {
if (Object.keys(portal).length === 0 && !isLoading) {
return null;
} else if (isLoading) {
return (
<div>
<p>loading…</p>
</div>
);
} else if (!!portal.id && !isLoading) {
return <div className="card-portal">data goes here</div>;
}
};
/*
* main component
*/
const PortalConfiguration = () => {
const [validated, setValidated] = useState(false);
const [code, setCode] = useState("");
const [env, setEnv] = useState("prod");
const [portalInfo, setPortalInfo] = useState({});
const [isLoading, setIsLoading] = useState(false);
const params = queryString.parse(location.search);
const onSubmitForm = (event) => {
const form = event.currentTarget;
setValidated(true);
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
return;
}
//re-initialize
setIsLoading(true);
setPortalInfo({});
axios
.get(`http://www.example.com/api`)
.then((response) => {
setIsLoading(false);
setPortalInfo({ ...response.data, ...{ baseUrl: baseUrl[env] } });
})
.catch((error) => console.log(error));
event.preventDefault();
event.stopPropagation();
};
useEffect(() => {
if (!!params && !!params.portal && !!params.env) {
if (!/^[a-zA-Z]{3}$/.test(params.portal) || (params.env !== "prod" && params.env !== "demo")) {
console.log(`Specified portal parameters {portal: ${params.portal}} and {env: ${params.env}} are incorrect`);
} else {
// this is where i want to set the portal and env states and submit the form
}
}
}, [params.portal]);
return (
<>
<h1>Your Portal Configuration</h1>
<Card>
<Card.Body>
<Form noValidate validated={validated} inline className="form-portal" onSubmit={onSubmitForm}>
<Form.Group className="form-group-code">
<label className="sr-only" htmlFor="code">
Portal Code
</label>
<Form.Control
type="text"
id="code"
value={code}
required
placeholder="Enter Portal Code (e.g. 'FWU')"
maxLength="3"
onChange={(e) => setCode(e.target.value)}
/>
<Form.Control.Feedback type="invalid">Portal Code must be three letters (a-z)</Form.Control.Feedback>
</Form.Group>
<Form.Group>
<ToggleButtonGroup type="radio" name="env" value={env} onChange={(val) => setEnv(val)}>
<ToggleButton type="radio" name="env" value="prod" variant="primary" className="btn-inline">
Production
</ToggleButton>
<ToggleButton type="radio" name="env" value="demo" variant="primary" className="btn-inline">
Demo
</ToggleButton>
</ToggleButtonGroup>
</Form.Group>
<Button variant="secondary" block="true" className="btn-inline" type="submit">
Submit
</Button>
</Form>
</Card.Body>
</Card>
<PortalDisplay data={portalInfo} isLoading={isLoading} env={env} />
</>
);
};
export default PortalConfiguration;
import React,{useState,useffect}来自“React”;
从“axios”导入axios;
从“查询字符串”导入查询字符串;
从“react引导/表单”导入表单;
从“反应引导/按钮”导入按钮;
从“react bootstrap/ToggleButtongGroup”导入ToggleButtongGroup;
从“react bootstrap/ToggleButton”导入ToggleButton;
从“反应引导/卡”导入卡;
/*
*完成后,此组件将显示微调器或API的结果
*/
const PortalDisplay=({data:portal,isLoading})=>{
if(Object.keys(portal.length==0&&!isLoading){
返回null;
}else if(正在加载){
返回(
加载和hellip
);
}否则如果(!!portal.id&&!isLoading){
返回数据在这里;
}
};
/*
*主要成分
*/
常量端口配置=()=>{
const[validated,setValidated]=useState(false);
const[code,setCode]=useState(“”);
const[env,setEnv]=useState(“prod”);
const[portalInfo,setPortalInfo]=useState({});
const[isLoading,setIsLoading]=useState(false);
const params=queryString.parse(location.search);
const onSubmitForm=(事件)=>{
const form=event.currentTarget;
setValidated(true);
if(form.checkValidity()==false){
event.preventDefault();
event.stopPropagation();
返回;
}
//重新初始化
设置加载(真);
setPortalInfo({});
axios
.得到(`http://www.example.com/api`)
。然后((响应)=>{
设置加载(假);
setPortalInfo({…response.data,{baseUrl:baseUrl[env]}});
})
.catch((错误)=>console.log(错误));
event.preventDefault();
event.stopPropagation();
};
useffect(()=>{
if(!!params&&!!params.portal&&!!params.env){
if(!/^[a-zA-Z]{3}$/.test(params.portal)| |(params.env!=“prod”&¶ms.env!=“demo”)){
log(`指定的门户参数{portal:${params.portal}}和{env:${params.env}}不正确');
}否则{
//这就是我想要设置门户和环境状态并提交表单的地方
}
}
},[params.portal]);
返回(
您的门户配置
门户代码
设置代码(e.target.value)}
/>
门户代码必须为三个字母(a-z)
setEnv(val)}>
生产
演示
提交
);
};
导出默认端口配置;
注释掉的一行说“这是我想设置门户和环境状态并提交表单的地方”,我知道我已经传递了querystring参数,需要设置这两种状态,然后提交表单
FWIW,对于如何处理
useState
的异步性,我已经考虑了通常的答案,即在useffect
中处理它,范围是您感兴趣的特定状态变量。这有两个问题:1)我有两个状态变量需要更新,因此我认为不能保证它们会按照我调用setters的顺序更新,从而创建一个可能的竞争条件;2)我不想每次code
或env
更新时都调用此代码,当用户手动与表单交互时会发生这种情况。我只希望在组件在加载时检测到查询字符串时自动提交它。是否将这两个字段组合成类似于useState({code:'',env:'prod
})`的选项?钩子版本,useState
一次只能处理一个状态变量。我想我可以把整个东西重构成一个类组件,使setState
可用,你可以一次设置多个状态。但您仍然会遇到问题,何时提交表单以确保状态已成功设置?是否可以将这两个字段组合成类似于useState({code:'',env:'prod
})`的选项?钩子版本,useState
一次只能处理一个状态变量。我想我可以把整个东西重构成一个类组件,使setState
可用,你可以一次设置多个状态。但您仍然遇到问题,何时提交表单以确保状态已成功设置?