如何在 setTimeout 中使用钩子?
How to use hooks inside setTimeout?
我有一个生成数组的简单程序。我已经添加了在单击按钮时反转数组的功能,但是当我单击反转时,显示的数组不会反转,而是保持原样。我正在使用 setTimeout
所以它可以被可视化。为什么没有出现可视化?
这是我的代码:
import React, { useState, useEffect } from 'react';
const Dummy2 = () => {
const [arr, setArr] = useState([]);
const [length, setLength] = useState(10);
useEffect(() => {
generateArray();
}, [length]);
const generateArray = () => {
const temp = [];
for(let i = 0; i < length; i++) {
temp.push(i + 1);
}
setArr(temp);
}
const handleLength = (e) => {
setLength(e.target.value);
}
const reverseArray = () => {
let delay = 1;
for(let i = 0; i < length / 2; i++) {
setTimeout(() => {
const temp = arr;
const t1 = arr[i];
const t2 = arr[length - 1 - i];
temp[i] = t2;
temp[length - 1 - i] = t1;
setArr(temp);
}, delay * 1);
delay++;
}
}
const printArray = () => {
console.log(arr);
}
const maxVal = Math.max(...arr);
return (
<div>
<div className="array-container" style={{height: '50%'}}>
{arr.map((value, idx) => (
<div className="array-element"
key={idx}
style={{height: `${(value * 100 / maxVal).toFixed()}%`,
width: `calc(${100 / length}% - 2px)`,
margin: '0 1px',
display: 'inline-block',
backgroundColor: 'black',
color: 'white'}}
>{value}</div>))
}
</div>
<div>
<button onClick={() => generateArray()}>New array</button>
<button onClick={() => reverseArray()}>Reverse</button>
<button onClick={() => printArray()}>Print array</button>
</div>
<div className="slider-container">
1
<input type="range"
min="1"
max="100"
onChange={(e) => handleLength(e)}
className="slider"
id="myRange"
/>
100
</div>
{length}
</div>
);
}
export default Dummy2;
在 reverseArray
函数中,您在每次迭代时设置状态,这会导致错误。
你需要完全反转数组,然后将其赋值给state。
Change you reverseArray
function like below:
const reverseArray = () => {
const temp = [...arr];
let delay = 1000;
for (let i = 0; i < length / 2; i++) {
setTimeout(() => {
const t1 = arr[i];
const t2 = arr[length - 1 - i];
temp[i] = t2;
temp[length - 1 - i] = t1;
setArr([...temp]);
}, delay);
delay+=1000;
}
}
工作示例:
编辑
插入 timeout
以在每次迭代中使用 1s
时间差进行可视化。
以下是我可以向您提出的建议:
import React, { Component } from "react";
import { render } from "react-dom";
import React, { useState, useEffect } from "react";
const Dummy2 = () => {
const [arr, setArr] = useState([]);
const [length, setLength] = useState(10);
const [reverseIndex, setReverseIndex] = useState(0);
const [didMount, setDidMount] = useState(true);
const [shouldReverseArray, setShouldReverseArray] = useState(true);
useEffect(() => {
generateArray();
}, [length]);
useEffect(() => {
// After clicking on 'reverse' button, reverseIndex is edited. Thus this effect is triggered because it listen to 'reverseIndex'. DidMount is here to prevent triggering the effect when the component is mounted
if (!didMount) {
if (shouldReverseArray && reverseIndex < length / 2) {
setTimeout(() => reverseArray(), 1000);
} else {
shouldReverseArray = setShouldReverseArray(false);
setReverseIndex(0);
}
} else {
setDidMount(false);
}
}, [reverseIndex]);
const generateArray = () => {
const temp = [];
for (let i = 0; i < length; i++) {
temp.push(i + 1);
}
setArr(temp);
};
const handleLength = e => {
setLength(e.target.value);
};
const reverseArray = () => {
const temp = Object.assign([], arr); // Make a copy of your state because it is not mutable
const tempStart = temp[reverseIndex];
const tempEnd = temp[length - tempStart];
temp[reverseIndex] = tempEnd;
temp[length - tempStart] = tempStart;
setArr(temp); // Set the new arr
setReverseIndex(prevState => ++prevState); // Set the new index
};
const printArray = () => {
console.log(arr);
};
const maxVal = Math.max(...arr);
return (
<div>
<div className="array-container" style={{ height: "50%" }}>
{arr.map((value, idx) => (
<div
className="array-element"
key={idx}
style={{
height: `${((value * 100) / maxVal).toFixed()}%`,
width: `calc(${100 / length}% - 2px)`,
margin: "0 1px",
display: "inline-block",
backgroundColor: "black",
color: "white"
}}
>
{value}
</div>
))}
</div>
<div>
<button onClick={() => generateArray()}>New array</button>
<button onClick={() => {setShouldReverseArray(true); reverseArray()}}>Reverse</button>
<button onClick={() => printArray()}>Print array</button>
</div>
<div className="slider-container">
1
<input
type="range"
min="1"
max="100"
onChange={e => handleLength(e)}
className="slider"
id="myRange"
/>
100
</div>
{length}
</div>
);
};
export default Dummy2;
render(<Dummy2 />, document.getElementById("root"));
这里是 Stackblitz repro.
请注意,当您更改数组的大小时,反向行为很奇怪,我不知道它是来自我的代码还是您的代码,我会尝试弄清楚。
此外,如果您不需要显示每个更改,您可以使用 .reverse()
,这样会更容易。
[编辑]:好的,这个错误来自 reverseIndex 没有被重置的事实。我编辑了代码以添加 'shouldReverseArray' 状态。我在 jsx 的 'onClick' 事件中调用它。很脏,但我不能在这里考虑其他事情。希望对您有所帮助:)
我有一个生成数组的简单程序。我已经添加了在单击按钮时反转数组的功能,但是当我单击反转时,显示的数组不会反转,而是保持原样。我正在使用 setTimeout
所以它可以被可视化。为什么没有出现可视化?
这是我的代码:
import React, { useState, useEffect } from 'react';
const Dummy2 = () => {
const [arr, setArr] = useState([]);
const [length, setLength] = useState(10);
useEffect(() => {
generateArray();
}, [length]);
const generateArray = () => {
const temp = [];
for(let i = 0; i < length; i++) {
temp.push(i + 1);
}
setArr(temp);
}
const handleLength = (e) => {
setLength(e.target.value);
}
const reverseArray = () => {
let delay = 1;
for(let i = 0; i < length / 2; i++) {
setTimeout(() => {
const temp = arr;
const t1 = arr[i];
const t2 = arr[length - 1 - i];
temp[i] = t2;
temp[length - 1 - i] = t1;
setArr(temp);
}, delay * 1);
delay++;
}
}
const printArray = () => {
console.log(arr);
}
const maxVal = Math.max(...arr);
return (
<div>
<div className="array-container" style={{height: '50%'}}>
{arr.map((value, idx) => (
<div className="array-element"
key={idx}
style={{height: `${(value * 100 / maxVal).toFixed()}%`,
width: `calc(${100 / length}% - 2px)`,
margin: '0 1px',
display: 'inline-block',
backgroundColor: 'black',
color: 'white'}}
>{value}</div>))
}
</div>
<div>
<button onClick={() => generateArray()}>New array</button>
<button onClick={() => reverseArray()}>Reverse</button>
<button onClick={() => printArray()}>Print array</button>
</div>
<div className="slider-container">
1
<input type="range"
min="1"
max="100"
onChange={(e) => handleLength(e)}
className="slider"
id="myRange"
/>
100
</div>
{length}
</div>
);
}
export default Dummy2;
在 reverseArray
函数中,您在每次迭代时设置状态,这会导致错误。
你需要完全反转数组,然后将其赋值给state。
Change you
reverseArray
function like below:
const reverseArray = () => {
const temp = [...arr];
let delay = 1000;
for (let i = 0; i < length / 2; i++) {
setTimeout(() => {
const t1 = arr[i];
const t2 = arr[length - 1 - i];
temp[i] = t2;
temp[length - 1 - i] = t1;
setArr([...temp]);
}, delay);
delay+=1000;
}
}
工作示例:
编辑
插入 timeout
以在每次迭代中使用 1s
时间差进行可视化。
以下是我可以向您提出的建议:
import React, { Component } from "react";
import { render } from "react-dom";
import React, { useState, useEffect } from "react";
const Dummy2 = () => {
const [arr, setArr] = useState([]);
const [length, setLength] = useState(10);
const [reverseIndex, setReverseIndex] = useState(0);
const [didMount, setDidMount] = useState(true);
const [shouldReverseArray, setShouldReverseArray] = useState(true);
useEffect(() => {
generateArray();
}, [length]);
useEffect(() => {
// After clicking on 'reverse' button, reverseIndex is edited. Thus this effect is triggered because it listen to 'reverseIndex'. DidMount is here to prevent triggering the effect when the component is mounted
if (!didMount) {
if (shouldReverseArray && reverseIndex < length / 2) {
setTimeout(() => reverseArray(), 1000);
} else {
shouldReverseArray = setShouldReverseArray(false);
setReverseIndex(0);
}
} else {
setDidMount(false);
}
}, [reverseIndex]);
const generateArray = () => {
const temp = [];
for (let i = 0; i < length; i++) {
temp.push(i + 1);
}
setArr(temp);
};
const handleLength = e => {
setLength(e.target.value);
};
const reverseArray = () => {
const temp = Object.assign([], arr); // Make a copy of your state because it is not mutable
const tempStart = temp[reverseIndex];
const tempEnd = temp[length - tempStart];
temp[reverseIndex] = tempEnd;
temp[length - tempStart] = tempStart;
setArr(temp); // Set the new arr
setReverseIndex(prevState => ++prevState); // Set the new index
};
const printArray = () => {
console.log(arr);
};
const maxVal = Math.max(...arr);
return (
<div>
<div className="array-container" style={{ height: "50%" }}>
{arr.map((value, idx) => (
<div
className="array-element"
key={idx}
style={{
height: `${((value * 100) / maxVal).toFixed()}%`,
width: `calc(${100 / length}% - 2px)`,
margin: "0 1px",
display: "inline-block",
backgroundColor: "black",
color: "white"
}}
>
{value}
</div>
))}
</div>
<div>
<button onClick={() => generateArray()}>New array</button>
<button onClick={() => {setShouldReverseArray(true); reverseArray()}}>Reverse</button>
<button onClick={() => printArray()}>Print array</button>
</div>
<div className="slider-container">
1
<input
type="range"
min="1"
max="100"
onChange={e => handleLength(e)}
className="slider"
id="myRange"
/>
100
</div>
{length}
</div>
);
};
export default Dummy2;
render(<Dummy2 />, document.getElementById("root"));
这里是 Stackblitz repro.
请注意,当您更改数组的大小时,反向行为很奇怪,我不知道它是来自我的代码还是您的代码,我会尝试弄清楚。
此外,如果您不需要显示每个更改,您可以使用 .reverse()
,这样会更容易。
[编辑]:好的,这个错误来自 reverseIndex 没有被重置的事实。我编辑了代码以添加 'shouldReverseArray' 状态。我在 jsx 的 'onClick' 事件中调用它。很脏,但我不能在这里考虑其他事情。希望对您有所帮助:)