#javascript #reactjs
Вопрос:
Я пытаюсь создать приложение, которое создаст компонент с 7-сегментным светодиодным дисплеем, но на светодиодном дисплее должен отображаться введенный номер. Я пытался найти в этом логику, но это не работает.
Логические:
- Для каждого введенного числа создайте компонент.
- Внутри этого компонента у него есть a
switch
, и в зависимости от введенного числа он добавитclassName
к каждому из них тот , который содержит отключенный стильdiv
.
И когда я пытаюсь ввести только одно число, оно отлично работает! Но когда я пытаюсь сделать это с двумя цифрами, это просто ошибка. Как вы можете видеть на изображениях:
У меня всего два компонента: <App/>
и <Numbers/>
.
Апп:
function App() {
const [input, setInput] = useState('');
const [number, setNumber] = useState([]);
const changeNumbers = () => {
// Splitting inputted numbers to add each number to array.
const inputSplit = input.toString().split('');
const inputArray = inputSplit.map(Number);
setNumber(inputArray);
}
return (
<div>
<input onChange={(e) => {setInput(e.target.value)}}/>
<button onClick={() => {changeNumbers()}}>Confirm</button>
{
number.length ?
number.map((singleNumber, idx) => (
<Numbers singleNumber={singleNumber} key={idx} />
))
:
<Numbers singleNumber={0} />
}
</div>
);
}
Числа:
const Numbers = ({ singleNumber }) => {
const switchDisabled = (element) => {
const segmentB = document.querySelector('.segment-b');
const segmentC = document.querySelector('.segment-c');
const segmentF = document.querySelector('.segment-f');
switch(element) {
case 2:
segmentC.classList.add('segment-disabled');
segmentF.classList.add('segment-disabled');
break;
case 6:
segmentB.classList.add('segment-disabled');
break;
default:
break;
}
}
useEffect(() => {
switchDisabled(singleNumber);
}, [singleNumber]);
return (
<div className="container">
<div className="segment segment-a"></div>
<div className="segment segment-b"></div>
<div className="segment segment-c"></div>
<div className="segment segment-d"></div>
<div className="segment segment-e"></div>
<div className="segment segment-f"></div>
<div className="segment segment-g"></div>
</div>
)
}
Стили:
.container {
width: 110px;
height: 160px;
position: relative; }
.segment {
position: absolute;
background-color: red; }
.segment-a {
top: 0;
left: 10px;
width: 90px;
height: 10px; }
.segment-b {
top: 13px;
right: 0;
height: 60px;
width: 10px; }
.segment-c {
bottom: 13px;
right: 0;
height: 60px;
width: 10px; }
.segment-d {
bottom: 0;
left: 10px;
width: 90px;
height: 10px; }
.segment-e {
bottom: 13px;
left: 0;
height: 60px;
width: 10px; }
.segment-f {
top: 13px;
left: 0;
height: 60px;
width: 10px; }
.segment-g {
top: 75px;
left: 16px;
width: 80px;
height: 10px; }
.segment-disabled {opacity: 0.1;}
Obs: Я не разместил imports
и exports
, чтобы сэкономить место.
Ответ №1:
Способ реагирования состоит в том, чтобы не манипулировать dom, вызывая узел dom, а создавать узлы dom. Поэтому вместо добавления классов путем вызова узла dom просто создайте узел dom с уже существующими классами.
В компоненте приложения вы можете просто задать входные данные в виде строки, а затем разделить их и пройтись по ним, поместив для каждого числовой компонент. В вашем компоненте number вы можете просто задать нужные сегменты в массиве и сопоставить их, разместив имена классов, которые вы хотите, когда компонент отображает.
Вот пример codesandbox со светодиодным дисплеем codesandbox
App.js
import { useState } from "react";
import Number from "./Number";
function App() {
const [input, setInput] = useState("0");
return (
<>
<input
onChange={(e) => {
setInput(e.target.value);
}}
/>
{input.split("").map((number, i) => (
<Number key={i} number={number} />
))}
</>
);
}
export default App;
Number.js
const Number = ({ number }) => {
let segments = [];
switch (number) {
case "1":
segments = ["b", "c"];
break;
case "2":
segments = ["a", "b", "d", "e", "g"];
break;
case "3":
segments = ["a", "b", "c", "d", "g"];
break;
case "4":
segments = ["b", "c", "f", "g"];
break;
case "5":
segments = ["a", "c", "d", "f", "g"];
break;
case "6":
segments = ["a", "c", "d", "f", "e", "g"];
break;
case "7":
segments = ["a", "b", "c"];
break;
case "8":
segments = ["a", "b", "c", "d", "e", "f", "g"];
break;
case "9":
segments = ["a", "b", "c", "d", "f", "g"];
break;
case "0":
segments = ["a", "b", "c", "d", "e", "f"];
break;
default:
segments = ["a", "b", "c", "d", "e", "f"];
}
return (
<div className="container">
{segments.map((segment) => (
<div key={segment} className={`segment segment-${segment}`}></div>
))}
</div>
);
};
export default Number;
Ответ №2:
Проблема в том, что document.querySelector
вызывается в Numbers
компоненте для получения сегмента, например
const segmentB = document.querySelector('.segment-b');
Этот вызов найдет первый элемент DOM на всей странице , содержащий класс segment-b
, который затем означает, что при отображении 2-го числа изменяются сегменты неправильного номера. Вы можете проверить это, введя двузначное число, где обе цифры совпадают, например, 66 — первое число должно быть отображено 6
, а 2-е число будет неправильным.
Вместо использования document.querySelector
с classList.add
и classList.remove
вы могли бы вместо этого использовать динамическую переменную для вычисления классов отключенных сегментов, необходимых для каждого числового компонента, которые затем отображаются в этом конкретном числовом компоненте:
const Numbers = ({ singleNumber }) => {
const switchDisabled = (element) => {
const allSegments = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
let disabledSegments = [];
switch (element) {
case 2:
disabledSegments.push("c");
disabledSegments.push("f");
break;
case 6:
disabledSegments.push("b");
break;
default:
break;
}
};
return (
<div className="container">
{allSegments.forEach(segmentLetter => {
const disabledClass = disabledSegments.includes(segmentLetter) ? 'segment-disabled' : '';
return (
<div className={`segment segment-${segmentLetter} ${disabledClass}`} />
)
})}
</div>
);
};
P.S. в общем, вы хотите в 99,99% случаев звонить document.querySelector
в react из-за подобных проблем