@Fehax

Как вернуть элемент массива, обернув его в, отобразить его в компоненте на лету?

Всем доброй ночи, а кто зашёл помочь днём - доброго дня!
Парни, пишу пет-приложение, которое измеряет скорость печати. Случайным образом выбираю с текстов, потом показываю текст в компоненте Tester.tsx с помощью .map().
Нужно реализовать такую фишку, не понимаю, как это сделать можно, не могу придумать алгоритм, а именно: когда пользователь вводит текст в input, в store как inputText, то обработчик на onChange должен проверять, если символ, который вводится в input === символу который мы должны напечатать(назовём его "печатный символ), то печатный символ должен оборачиваться в span, и на лету рендерится в компоненте, но уже обернутым в span, оборачиваться в тег должно по-символьно, вот так. Здесь видео - ССЫЛКА

Redux Toolkit Store
import {createSlice} from "@reduxjs/toolkit"
import type {PayloadAction} from "@reduxjs/toolkit"

interface TyperState {
	texts: string[]
	words: string[]
	text: string
	statusTest: boolean
	timeStart: number
	chArray: string[]
	key: string
	completed: boolean
	countCharacters: number
	inputText: string
	completeWords: string[]
	accuracy: number
	time: number
}

const initialState: TyperState = {
	chArray: [],
	completed: false,
	completeWords: [],
	inputText: "",
	text: "",
	words: [],
	texts: [
		`You never read a book on psychology, Tippy. You didn't need to. You knew by some divine instinct that you can make more friends in two months by becoming genuinely interested in other people than you can in two years by trying to get other people interested in you.`,
		`I know more about the private lives of celebrities than I do about any governmental policy that will actually affect me. I'm interested in things that are none of my business, and I'm bored by things that are important to know.`,
		`A spider's body consists of two main parts: an anterior portion, the prosoma (or cephalothorax), and a posterior part, the opisthosoma (or abdomen).`,
		`As customers of all races, nationalities, and cultures visit the Dekalb Farmers Market by the thousands, I doubt that many stand in awe and contemplate the meaning of its existence. But in the capital of the Sunbelt South, the quiet revolution of immigration and food continues to upset and redefine the meanings of local, regional, and global identity.`,
		`Outside of two men on a train platform there's nothing in sight. They're waiting for spring to come, smoking down the track. The world could come to an end tonight, but that's alright. She could still be there sleeping when I get back.`,
		`I'm a broke-nose fighter. I'm a loose-lipped liar. Searching for the edge of darkness. But all I get is just tired. I went looking for attention. In all the wrong places. I was needing a redemption. And all I got was just cages.`,
	],
	key: "",
	timeStart: 0,
	accuracy: 0,
	countCharacters: 0,
	time: 0,
	statusTest: false,
}

const typerSlice = createSlice({
	name: "typer",
	initialState,
	reducers: {
		startTyping(state) {
			const text = state.texts[Math.floor(Math.random() * state.texts.length)]
			state.words = text.split(" ")
			state.text = text
			state.statusTest = true
			state.timeStart = Date.now()
			state.chArray = text.split("")
		},
		keyboardHandler(state, action: PayloadAction<string>) {
			state.key = action.payload
		},
	},
})

export default typerSlice.reducer
export const {startTyping, keyboardHandler} = typerSlice.actions


Component Tester.tsx

import React, {useEffect} from "react"
import {useAppDispatch, useAppSelector} from "../hooks/hooks"
import "./styles/Tester.scss"
import {keyboardHandler, startTyping} from "../store/typer.slice"

const Tester: React.FC = () => {
	const accuracy = useAppSelector((state) => state.typer.accuracy)
	const text = useAppSelector(({typer: {text}}) => Array.from(text))
	const time = useAppSelector((state) => state.typer.time)
	const words = useAppSelector((s) => s.typer.words)
	const completeWords = useAppSelector((s) => s.typer.completeWords)
	const key = useAppSelector((state) => state.typer.key)
	const characters = useAppSelector((state) => state.typer.countCharacters)
	const inputText = useAppSelector((s) => s.typer.inputText)
	const dispatch = useAppDispatch()

	useEffect(() => {
		const keyHandler = (event: KeyboardEvent) => {
			dispatch(keyboardHandler(event.key))
		}
		dispatch(startTyping())
		document.addEventListener("keydown", keyHandler)
		return () => {
			document.removeEventListener("keydown", keyHandler)
		}
	}, [])

	const inputOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
		const value = e.target.value
	}

	return (
		<div className="testWrapper">
			<div>
				<p className="textForTest">
					{text.map((ch, index) => {
						return <span key={index}>{ch}</span>
					})}
				</p>
				<input
					value={inputText}
					onChange={inputOnChange}
					type="text"
					placeholder="text"
				/>
				<p>Last character: {key}</p>
				<div className="typeStatistics">
					<span className="statisticsItem">Accuracy: {accuracy}%</span>
					<span className="statisticsItem">
						Characters per minute: {characters}
					</span>
					<span className="statisticsItem">Time: {time} s.</span>
				</div>
			</div>
			<div className="controls">
				<button>Start</button>
				<button>Restart</button>
			</div>
		</div>
	)
}

export default Tester
  • Вопрос задан
  • 54 просмотра
Решения вопроса 1
i229194964
@i229194964
Веб разработчик
Tester.tsx
import React, { useEffect, useState } from "react";
import { useAppDispatch, useAppSelector } from "../hooks/hooks";
import "./styles/Tester.scss";
import { keyboardHandler, startTyping } from "../store/typer.slice";

const Tester: React.FC = () => {
  const accuracy = useAppSelector((state) => state.typer.accuracy);
  const text = useAppSelector(({ typer: { text } }) => Array.from(text));
  const time = useAppSelector((state) => state.typer.time);
  const words = useAppSelector((s) => s.typer.words);
  const completeWords = useAppSelector((s) => s.typer.completeWords);
  const key = useAppSelector((state) => state.typer.key);
  const characters = useAppSelector((state) => state.typer.countCharacters);
  const inputText = useAppSelector((s) => s.typer.inputText);
  const dispatch = useAppDispatch();

  const [printedText, setPrintedText] = useState(""); // Новое состояние для хранения текста с обернутыми символами

  useEffect(() => {
    const keyHandler = (event: KeyboardEvent) => {
      dispatch(keyboardHandler(event.key));
    };
    dispatch(startTyping());
    document.addEventListener("keydown", keyHandler);
    return () => {
      document.removeEventListener("keydown", keyHandler);
    };
  }, []);

  const inputOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    let newPrintedText = "";
    for (let i = 0; i < text.length; i++) {
      if (i < value.length) {
        // Проверка на наличие символа в введенном тексте
        if (text[i] === value[i]) {
          newPrintedText += `<span class="highlight">${text[i]}</span>`; // Обернуть символ в <span> с классом highlight
        } else {
          newPrintedText += text[i]; // Добавить символ без обертки
        }
      } else {
        newPrintedText += text[i]; // Добавить символ без обертки
      }
    }
    setPrintedText(newPrintedText);
  };

  return (
    <div className="testWrapper">
      <div>
        <p
          className="textForTest"
          dangerouslySetInnerHTML={{ __html: printedText }} // Используйте dangerouslySetInnerHTML для отображения HTML-разметки
        />
        <input
          value={inputText}
          onChange={inputOnChange}
          type="text"
          placeholder="text"
        />
        <p>Last character: {key}</p>
        <div className="typeStatistics">
          <span className="statisticsItem">Accuracy: {accuracy}%</span>
          <span className="statisticsItem">
            Characters per minute: {characters}
          </span>
          <span className="statisticsItem">Time: {time} s.</span>
        </div>
      </div>
      <div className="controls">
        <button>Start</button>
        <button>Restart</button>
      </div>
    </div>
  );
};

export default Tester;

и вы можете определить стили в Tester.scss.
.highlight {
  background-color: yellow;
}
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы