Javascript React setState hook未在循环外更新
更新: 这就是一个例子 我有一个textarea组件,我在其中逐行执行验证: UI中的错误消息是一组对象,这些对象具有基于文本所在行的id属性和包含错误的消息属性<代码>例如{id:1,消息:“错误消息”,名称:“第行文本”} 据我所知,不能在循环中设置状态,因为结果不能保证 这是获取字符串的setMessage函数: 正在我的验证函数内调用setMessage:Javascript React setState hook未在循环外更新,javascript,reactjs,for-loop,react-hooks,setstate,Javascript,Reactjs,For Loop,React Hooks,Setstate,更新: 这就是一个例子 我有一个textarea组件,我在其中逐行执行验证: UI中的错误消息是一组对象,这些对象具有基于文本所在行的id属性和包含错误的消息属性例如{id:1,消息:“错误消息”,名称:“第行文本”} 据我所知,不能在循环中设置状态,因为结果不能保证 这是获取字符串的setMessage函数: 正在我的验证函数内调用setMessage: function setMessage(data) { console.log('data', data); consol
function setMessage(data) {
console.log('data', data);
console.log("arrFromVariableTypeNameString ", arrFromVariableTypeNameString);
let allMessages = [...messagesContainer];
function drop(data, func) {
var result = [];
for (var i = 0; i < data.length; i++) {
var check = func(data[i]);
console.log("check ", check);
if (check) {
console.log("i + 1 ", i + 1);
result = data.slice(i, i + 1);
break;
}
}
return result;
}
for (var i = 0; i < arrFromVariableTypeNameString.length; i++) {
var match = drop(allMessages, e => e.id === i + 1);
if (match?.length) {
match[0] = {
...match[0],
...{
message: data,
name: arrFromVariableTypeNameString[i]
}
}
console.log("match ", match);
console.log("allMessages ", allMessages);
allMessages = allMessages.map(t1 => ({
...t1,
...match.find(t2 => {
console.log("t2.id === t1.id ", t2.id === t1.id);
return t2.id === t1.id
})
}))
} else {
allMessages.push({
name: arrFromVariableTypeNameString[i],
id: i + 1,
message: data
})
}
}
setMessagesContainer(allMessages)
}
函数设置消息(数据){
console.log('data',data);
log(“arrFromVariableTypeNameString”,arrFromVariableTypeNameString);
让allMessages=[…messagesContainer];
函数删除(数据,函数){
var结果=[];
对于(变量i=0;ie.id==i+1);
如果(匹配?长度){
匹配[0]={
…匹配[0],
...{
信息:数据,
名称:arrFromVariableTypeNameString[i]
}
}
控制台日志(“匹配”,匹配);
日志(“所有消息”,所有消息);
allMessages=allMessages.map(t1=>({
…t1,
…匹配。查找(t2=>{
log(“t2.id==t1.id”,t2.id==t1.id);
返回t2.id==t1.id
})
}))
}否则{
allMessages.push({
名称:arrFromVariableTypeNameString[i],
id:i+1,
信息:数据
})
}
}
设置消息容器(所有消息)
}
这是整个组件:
导出函数变量setupmodal({
现有可变类型
}) {
const dispatch=usedpatch();
常量[isOpen,setIsOpen]=useState();
const[variableTypeName,setVariableTypeName]=useState(“”);
常量[clipboardData,setClipboardData]=useState(“”)
常量[pasted,setIsPasted]=useState(false)
const[messages,setMessages]=useState(“”);
const[messagesContainer,setMessagesContainer]=useState([]);
var arrFromVariableTypeNameString=variableTypeName.split('\n');
useffect(()=>{
函数设置消息(数据){
console.log('data',data);
log(“arrFromVariableTypeNameString”,arrFromVariableTypeNameString);
让allMessages=[…messagesContainer];
函数删除(数据,函数){
var结果=[];
对于(变量i=0;ie.id==i+1);
如果(匹配?长度){
匹配[0]={…匹配[0],
...{
信息:数据,
名称:arrFromVariableTypeNameString[i]
}
}
控制台日志(“匹配”,匹配);
日志(“所有消息”,所有消息);
allMessages=allMessages.map(t1=>({…t1,
…匹配。查找(t2=>{
log(“t2.id==t1.id”,t2.id==t1.id);
返回t2.id==t1.id
})
}))
}否则{
allMessages.push({
名称:arrFromVariableTypeNameString[i],
id:i+1,
信息:数据
})
}
}
设置消息容器(所有消息)
}
函数验证器(variableType){
风险值数据={
variableType:variableType,
}
风险值规则={
变量类型:“正则表达式:^[a-zA-Z0-9+$|最小值:3 |最大值:20”,
}
变量消息={
min:`至少输入三个字符。`,
马克斯:“不要超过二十个字符。”,
regex:`不允许使用特殊字符(但空格)`
}
验证(数据、规则、消息)
。然后(成功=>{
console.log('变量类型输入正确',成功)
setMessage(“”);
返回
})
.catch(错误=>{
console.log('error',error)
setMessage(错误[0]。消息);
返回
});
}
函数检查阵列唯一(myArray){
if(myArray.length==50)setMessages('只允许50种变量类型');
返回myArray.length==新集合(myArray.size);
}
arrFromVariableTypeNameString.map((variableType,i,thisArr)=>{
函数查找副本(唯一计数){
变量计数={},
结果='';
uniqueCount.forEach((i)=>{
计数[i]=(计数[i]| 0)+1;
});
控制台日志(计数);
返回Object.keys(count).map((k)=>{
if(count[k]>1)返回result.concat(`Variable Type${k}:出现${count[k]}次。`)
}).filter((项)=>项!==未定义)
}
如果(检查阵列唯一性(此阵列)){
if(验证器(可变类型)){
const setMessageForLine = (message, lineNumber) => {
setMessagesContainer((existing) => [
...existing.slice(0, lineNumber),
message,
...existing.slice(lineNumber + 1)
]);
}
import "./styles.css";
import React, { useEffect, useState, useRef, useCallback } from "react";
import { validate } from "indicative/validator";
export function TextArea({ onSave }) {
const [variableTypeName, setVariableTypeName] = useState("");
const [clipboardData, setClipboardData] = useState("");
const [pasted, setIsPasted] = useState(false);
const [messagesContainer, setMessagesContainer] = useState([]);
// don't need to validate the same text more than once
const lastCheckedLines = useRef([]);
const setMessageForLine = useCallback(
(message, lineNumber) => {
setMessagesContainer((existing) => [
...existing.slice(0, lineNumber),
message,
...existing.slice(lineNumber + 1)
]);
},
[setMessagesContainer]
);
const getLineError = useCallback(
(text, index, all) => {
// if too many lines
if (index >= 50) {
return "Only 50 Variable Types allowed.";
}
// blank lines will show up as duplicates of each other
if (text.length === 0) {
return "No empty lines";
}
// check if this line is the same as any of the previous
const duplicateOf = all.slice(0, index).findIndex((v) => v === text);
if (duplicateOf !== -1) {
return `Duplicate of line ${duplicateOf + 1}`;
}
},
[]
);
const asyncValidateLine = useCallback(
(text, index) => {
var data = {
variableType: text
};
var rules = {
variableType: "regex:^[a-zA-Z0-9_ ]+$|min:3|max:20"
};
var messages = {
min: `Enter at least three characters.`,
max: `Don't exceed more than twenty characters.`,
regex: `No special characters (but spaces) allowed.`
};
validate(data, rules, messages)
.then((success) => {
console.log("Variable Type Entered correctly.", success);
setMessageForLine("", index);
})
.catch((error) => {
console.log("error", error);
setMessageForLine(error[0].message, index);
});
},
[setMessageForLine]
);
useEffect(() => {
const lineTexts = variableTypeName.split("\n");
// remove extra lines when deleting
setMessagesContainer((existing) =>
existing.length > lineTexts.length
? existing.slice(0, lineTexts.length)
: existing
);
lineTexts.forEach((text, i) => {
// only check if we have a new text
if (text !== lastCheckedLines.current[i]) {
console.log(`evaluating line ${i + 1}`);
const error = getLineError(text, i, lineTexts);
if (error) {
setMessageForLine(error, i);
} else {
asyncValidateLine(text, i);
}
}
});
lastCheckedLines.current = lineTexts;
}, [variableTypeName, getLineError, asyncValidateLine, setMessageForLine, setMessagesContainer]);
const handlePaster = (e) => {
e.persist();
setIsPasted(true);
setClipboardData(e.clipboardData.getData("text"));
};
const handleChange = (e) => {
e.persist();
var { keyCode } = e;
var { value } = e.target;
if (keyCode === 13) {
setVariableTypeName(`${value}\n`);
return;
} else if (pasted === true && keyCode === 13) {
setVariableTypeName(`${variableTypeName.concat(clipboardData)}\n`);
setIsPasted(false);
return;
} else if (pasted === true && keyCode !== 13) {
setVariableTypeName(`${variableTypeName.concat(clipboardData)}`);
setIsPasted(false);
return;
} else {
setVariableTypeName(`${value}`);
return;
}
};
return (
<div>
<textarea
placeholder="Variable Types"
maxLength={100}
value={variableTypeName}
onPaste={(e) => handlePaster(e)}
onChange={(e) => handleChange(e)}
/>
{messagesContainer.map((message, i, arr) => {
console.log("message ", message);
return message ? (
<p key={i} className="Messages">{`Error on line ${
i + 1
}: ${message}`}</p>
) : null;
})}
</div>
);
}
export default function App() {
return (
<div className="App">
<TextArea onSave={console.log} />
</div>
);
}
import "./styles.css";
import React, { useEffect, useState, useCallback } from "react";
export function TextArea({ onSave }) {
const [variableTypeName, setVariableTypeName] = useState("");
const [clipboardData, setClipboardData] = useState("");
const [pasted, setIsPasted] = useState(false);
const [messagesContainer, setMessagesContainer] = useState([]);
const getLineError = useCallback(
(text, index, all) => {
// if too many lines
if (index >= 50) {
return "Only 50 Variable Types allowed.";
}
if (text.length < 3) {
return `Enter at least three characters.`;
}
if (text.length > 20) {
return `Don't exceed more than twenty characters.`;
}
if (!text.match(/^[a-zA-Z0-9_ ]+$/)) {
return `No special characters (but spaces) allowed.`;
}
// check if this line is the same as any of the previous
const duplicateOf = all.slice(0, index).findIndex((v) => v === text);
if (duplicateOf !== -1) {
return `Duplicate of line ${duplicateOf + 1}`;
}
return "";
},
[]
);
useEffect(() => {
const lineTexts = variableTypeName.split("\n");
setMessagesContainer(lineTexts.map(getLineError));
}, [variableTypeName, getLineError, setMessagesContainer]);
const handlePaster = (e) => {
e.persist();
setIsPasted(true);
setClipboardData(e.clipboardData.getData("text"));
};
const handleChange = (e) => {
e.persist();
var { keyCode } = e;
var { value } = e.target;
if (keyCode === 13) {
setVariableTypeName(`${value}\n`);
return;
} else if (pasted === true && keyCode === 13) {
setVariableTypeName(`${variableTypeName.concat(clipboardData)}\n`);
setIsPasted(false);
return;
} else if (pasted === true && keyCode !== 13) {
setVariableTypeName(`${variableTypeName.concat(clipboardData)}`);
setIsPasted(false);
return;
} else {
setVariableTypeName(`${value}`);
return;
}
};
return (
<div>
<textarea
placeholder="Variable Types"
maxLength={100}
value={variableTypeName}
onPaste={(e) => handlePaster(e)}
onChange={(e) => handleChange(e)}
/>
{messagesContainer.map((message, i, arr) => {
console.log("message ", message);
return message ? (
<p key={i} className="Messages">{`Error on line ${
i + 1
}: ${message}`}</p>
) : null;
})}
</div>
);
}
export default function App() {
return (
<div className="App">
<TextArea onSave={console.log} />
</div>
);
}