javascript - วิธีตั้งค่าผลลัพธ์การพิมพ์หลายบรรทัดใน react พร้อมดีเลย์ระหว่างบรรทัด
ฉันได้สร้างคอมโพเนนต์แบบคลาสใน React ที่เริ่มเอฟเฟกต์การพิมพ์หลายบรรทัดเมื่อโหลด
มันใช้งานได้ดี แต่ปัญหาคือสายทั้งสองเริ่มพิมพ์พร้อมกัน ซึ่งสมเหตุสมผลเมื่อพิจารณาจากลักษณะการทำงานของ setTimeout
ดังนั้นฉันจึงกำลังมองหาไอเดียหรือคำแนะนำใด ๆ เกี่ยวกับวิธีทำให้มันรอจนกว่าบรรทัดแรกจะพิมพ์เสร็จก่อนที่จะไปบรรทัดถัดไป
import React from 'react'
import TerminalCursor from 'icons/TerminalCursor'
import TerminalPrompt from 'icons/TerminalPrompt'
import { v4 as uuidv4 } from 'uuid'
type MyProps = {}
type MyState = {
introArray: string[]
}
class Terminal extends React.Component<MyProps, MyState> {
state: MyState = {
introArray: [],
}
componentDidMount() {
// Data to import from sanity
const starterArray = [
'The very first line that needs to finish typing before going to the next line',
'The second line that needs to start being typed after the first line',
]
const createTypingEffect = (text: string, index: number) => {
for (let i = 0; i < text.length; i++) {
setTimeout(() => {
let arrayCopy = this.state.introArray.slice()
this.setState((state) => ({
introArray: [...state.introArray.slice(0, index), arrayCopy[index] + text[i], ...state.introArray.slice(index + 1)],
}))
}, 100 * i)
}
}
starterArray.forEach((starterText, starterIndex) => {
// Setting empty string for each line in starterArray so we dont get undefined as first character
this.setState((state) => ({
introArray: [...state.introArray, ''],
}))
// Need to wait for first line to finish typing before starting the second line
createTypingEffect(starterText, starterIndex)
})
}
render() {
return (
<div className="w-1/2 h-1/2 p-5 flex items-start justify-start bg-clip-padding bg-slate-900 backdrop-filter backdrop-blur-xl bg-opacity-60 border border-gray-900 rounded">
<div className="flex flex-col">
{this.state.introArray.map((introLine) => (
<div className="flex items-center" key={uuidv4()}>
<TerminalPrompt />
<p className="text-white">{introLine}</p>
</div>
))}
{/* Actual prompt starts here */}
<div className="flex">
<TerminalPrompt />
<TerminalCursor />
</div>
</div>
</div>
)
}
}
export default Terminal
นี่คือการสาธิต sandbox (ไม่แน่ใจว่าทำไมตัวอักษรบางตัวถึงถูกทำซ้ำใน sandbox แต่ไม่มีปัญหานี้ในเครื่องของตัวเอง...)
https://codesandbox.io/s/recursing-star-iohjw9?file=/src/Terminal.tsx
คำตอบ #1
คุณจำเป็นต้องใช้แนวทางแบบอะซิงโครนัสสำหรับงานประเภทนี้ (ฉันไม่ได้ตรวจสอบตรรกะเอฟเฟกต์การพิมพ์ของคุณ แค่ทำให้กระบวนการทั้งหมดเป็นแบบอะซิงโครนัส)
import React from "react";
type MyProps = {};
type MyState = {
introArray: string[];
};
class Terminal extends React.Component<MyProps, MyState> {
state: MyState = {
introArray: []
};
componentDidMount() {
// Data to import from sanity
const starterArray = [
"The very first line that needs to finish typing before going to the next line",
"The second line that needs to start being typed after the first line"
];
const createTypingEffect = async (text: string, index: number) => {
return Promise.all(
text.split("").map(
(c, i) =>
new Promise((res) => {
setTimeout(() => {
let arrayCopy = this.state.introArray.slice();
this.setState((state) => ({
introArray: [
...state.introArray.slice(0, index),
arrayCopy[index] + c,
...state.introArray.slice(index + 1)
]
}));
res(null);
}, 100 * i);
})
)
);
};
const cycle = async () => {
let i = 0;
for (const starterText of starterArray) {
// Setting empty string for each line in starterArray so we dont get undefined as first character
this.setState((state) => ({
introArray: [...state.introArray, ""]
}));
await createTypingEffect(starterText, i);
i++;
}
};
cycle();
}
render() {
console.log(this.state.introArray);
return (
<div className="w-1/2 h-1/2 p-5 flex items-start justify-start bg-clip-padding bg-slate-900 backdrop-filter backdrop-blur-xl bg-opacity-60 border border-gray-900 rounded">
<div className="flex flex-col">
{this.state.introArray.map((introLine, index) => (
<div className="flex items-center" key={index}>
<p className="text-white">{introLine}</p>
</div>
))}
</div>
</div>
);
}
}
export default Terminal;
คุณสามารถใช้หลายวิธีเพื่อจัดการกับตรรกะแบบอะซิงค์ได้ ผมใช้วิธี Promise.all ที่นี่ โดยให้เวลาเพิ่มขึ้นกับ timeout เพียงเพื่อทำตามสิ่งที่คุณกำลังทำ วิธีแก้ปัญหาอีกวิธีหนึ่งคือการใช้ลูป for...of แบบง่าย ๆ และรอ promisified timeout ภายใน แบบนั้นคุณไม่จำเป็นต้องให้เวลาเพิ่มขึ้นกับ timeout เพราะพวกมันจะถูกดำเนินการ ค่อนข้างพร้อมกัน
ตัวอย่างการทำงานอยู่ที่นี่ (ไม่สามารถเพิ่มโค้ดตัวอย่างที่ใช้งานได้ที่นี่ เนื่องจาก SO ไม่มีการรองรับ TS)
