Мне нравится видеть, как мой код превращается на экране во что-то красивое и интерактивное. Но когда вы разрабатываете что-то в одиночку или в небольшой группе, вы должны носить много шляп как инженер, дизайнер, автор и менеджер проекта.
Когда я чувствую себя подавленным перед тем, как погрузиться в большой проект, возникает искушение расслабиться и сначала заняться интересной частью, а затем начать набрасывать макеты и выбирать цветовые схемы. Но когда я оставляю сложную часть напоследок, не представляя, как я это сделаю, я впадаю в полную панику: «КАК Я ЭТО СДЕЛАЮ??».
Вот почему мое личное правило — всегда сначала делать уродливую часть.
Вот недавний проект, который я сделал с использованием популярного JavaScript-фреймворка React:
React — популярный фреймворк для создания одностраничных приложений. Приложение разделено на отдельные компоненты. Каждый компонент хранит данные в своем собственном внутреннем состоянии. Когда состояние обновляется, этот компонент повторно отображается в DOM без обновления всей страницы. Это обеспечивает быстрый, привлекательный и удобный пользовательский интерфейс.
Этот проект представляет собой приложение-викторину, подобное тому, что вы видите на популярных сайтах, на которые все любят тратить время. Пользователь отвечает на несколько вопросов, и приложение сообщает им, что их ответы говорят об их личности, предпочтениях или чем-то еще. Я знал, что хочу, чтобы он выглядел счастливым и веселым, с плавными переходами, которые придают ему современный вид, и, прежде всего, без лишних кнопок. Но прежде всего я хотел, чтобы это работало! Остальное не имеет значения, если я не могу принимать ответы в качестве входных данных и выдавать результат в качестве вывода. Итак, сначала я сделал уродливую часть.
Мне нужен был алгоритм, чтобы сделать две вещи. Во-первых, он должен был отслеживать ответы пользователей. Во-вторых, он должен был рассчитать результаты на основе этих ответов.
Этот стиль викторины широко распространен в Интернете, но логика для него возникла в журналах. Для каждого вопроса читатель выбирал вариант (A, B, C или D) для каждого вопроса. В конце они складывали числа A, B, C и D, и их результатом было то, что соответствовало букве с наибольшим количеством очков. Я следовал этой технике при построении логики своего приложения. Во-первых, я создал следующий объект в качестве викторины-заполнителя, с фактическими вопросами, которые нужно будет заполнить позже (еще один пример того, как сначала сделать уродливую часть):
let quiz = { title: "which whatever are you?", questions: [ {question: "What is the first thing?", answers: ["a-ish", "b-ish", "c-ish", "d-ish"] }, {question: "What is the second thing?", answers: ["eey", "bee", "sea", "dee"] }, {question: "What is the third thing?", answers: ["the a one", "the b one", "the c one", "the d one"] } ], results : ["A", "B", "C", "D"] }
Индекс вариантов напрямую соответствует индексу результатов. Таким образом, если пользователь выбирает в основном варианты с индексом 1, его результатом будет тот, который находится в массиве результатов с индексом 1.
Затем мне нужно отобразить его в DOM. Вышеупомянутый тест состоит из трех вопросов, на каждый из которых есть четыре возможных ответа. Но мне нужно только правильно написать код для одного вопроса и одного ответа. Это потому, что React (и его гибридный язык html-JavaScript JSX) делает это простым. Используя метод Array.map()
, я могу отображать один и тот же код в DOM один раз для каждого вопроса в массиве «questions». Я снова использую этот метод для отображения каждого варианта в массиве «ответы». Итак, вся викторина отображается в DOM, используя всего несколько строк кода ниже:
{quiz.questions.map( x => { return ( <div key={x.question}> <div > {x.question} </div> <div> {x.answers.map( y => <div key={y} onClick={ () => keepScore( x.answers.indexOf(y), quiz.questions.indexOf(x) )}> {y}</div>)} </div> </div> )})}
Здесь нет стиля. Опять же, сначала я делаю уродливую часть.
В приведенном выше коде есть прослушиватель событий, который использует синтаксис React onClick. Это вызывает следующую функцию:
function keepScore(index, questionNum){ const scoreCopy = {...scoreSheet} // increase the score based on the answer selected if (Object.keys(scoreCopy).includes(index.toString()){ scoreCopy[index]++} else { scoreCopy[index] = 1 } // put this new score in the component's state setScoreSheet(scoreCopy) // if it's the last question call the function to add up the score if (questionNum == (quiz.questions.length-1).toString()){ addScore(scoreCopy)} }
Эта функция берет индекс вопроса из массива «вопросы» и индекс варианта ответа из массива «ответы». Затем он обновляет оценку, добавляя «балл» к объекту, хранящемуся в состоянии этого компонента React, где ключ — это индекс выбранного параметра, а значение — количество баллов, которые он имеет.
const [scoreSheet, setScoreSheet] = useState({})
Наконец, если вопрос, на который в данный момент отвечает последний вопрос в викторине, будет вызвана другая функция, которая подсчитывает баллы. Написание таким образом позволяет каждой викторине иметь разное количество вопросов.
Окончательный результат рассчитывается следующим образом:
function addScore(finalScore){ const highScore = Math.max(...Object.values(finalScore)) let winners = Object.keys(finalScore).filter( x => finalScore[x] == highScore) console.log(quiz.results[winners[0]] + " score: " + highScore) }
Эта функция использует встроенный метод JavaScript Math.max(), чтобы определить, какой из индексов ответов имеет наивысший балл. Затем он записывает результат с тем же индексом в консоль (или, в конечном проекте, в DOM).
Вот и все. Тяжелая часть позади. Приведенный выше код ничего не добавляет в DOM, кроме текста вопросов и их вариантов. В конце ничего не видно, кроме `console.log()`. Но это самое сердце приложения. Когда сначала делаешь уродливую часть, все после этого просто течет. В итоге будет выглядеть так: