Передаем состояние

В прошлом уроке мы научились работать с текстовым вводом от пользователя.

Давайте еще немного улучшим UX: будем закрашивать кнопку в красный цвет, если человек ответил неправильно, и в зеленый - если правильно. Для начала возьмем тот код, который мы создали в прошлом уроке, но чтобы упростить пример, уберем кнопку произвольного ответа и вернем правильный ответ в список.

Вы можете продолжить в question.tsx, или создать новый файл.

// Import showToast action
import {showToast} from '@app/ui'

const question = {
  label: 'The Capital of Great Britain',
  answers: [ 'New York', 'Liverpool', 'London' ],
  correct: 'London',
}

// Screen with 3 buttons and question
const questionScreen = app.screen('/', async function(ctx, req) {
  return <screen title="Question">

    <text class="section">{question.label}</text>

    {question.answers.map( answer =>
      <button
        onClick={buttonHandler.apiCall({ value: answer })}
        class={["secondary","section"]}
        title={answer}
      />
    )}
  </screen>
})

// Handler for button
const buttonHandler = app.apiCall('/check', async function(ctx, req) {
  return  ( req.body.value === question.correct )
    ? showToast("Correct")
    : showToast(req.body.value + ' is not correct');
})

Напомним, что делает этот код: он показывает вопрос и варианты ответа на него. При клике на правильный вариант он выдает сообщение "Correct", на неправильный - выдает сообщение об ошибке.


Итак, наша задача - сделать так чтобы при клике на кнопку ответа она меняла цвет. В классическом React-подходе это смена состояния у экрана - React меняет состояние на клиенте и применяет его тут-же.

В нашем случае важно понять одну вещь:

В Chatium нет клиентского состояния

Если мы хотим изменить состояние экрана - мы должны выдать новый экран с сервера. Изменить состояние можно либо через изменение данных, либо просто измененив URL.

Например, можно добавить что-нибудь в URL query-сегмент - то что идет в адресе после символа вопроса.

В самом коде экрана можно посмотреть на объект req.query . В него приходит query из HTTP-запроса. То есть если мы передадим в адресе ?q=value1 , то в коде экрана сможем получить это значение через  req.query.q .

Давайте поменяем обработчик нажатия кнопки таким образом, чтобы он возвращал нас на экран с индикацией ответа.

// Handler for button
const buttonHandler = app.apiCall('/check',async function(ctx, req){
  const messageAction = ( req.body.value === question.correct )
    ? showToast("Correct")
    : showToast(req.body.value + ' is not correct');
  const redirectAction = questionScreen.navigate(
    {queryParams: {'answered': req.body.value}, replace: true}
    );
  return [messageAction, redirectAction];
})

В этом коде мы объявляем 2 действия и возвращаем их в массиве. Приложение выполнит их последовательно: сначала покажет сообщение, а потом сделает переход.

Обратите внимание на второй аргумент в navigate. В нем мы передаем {replace: true}, что делает замену текущего экрана, вместо открытия нового.

Теперь выведем значение answered в экране, чтобы убедиться, что все сделали правильно.

Добавим вывод ответа под вопросом.

<text class="section">{question.label}</text>
  {req.query?.answered && <text class="section">
  Answered: {req.query.answered}
  </text>}

Обратите внимание на конструкцию {req.query?.answered && ...}. Она позволяет выводить тег &lt;text&gt;...&lt;/text&gt; только если req.query существует и req.query.answered что-то содержит.

Теперь давайте перенесем эту логику в раскрашивание кнопки.

Сейчас, кнопка каждого из вариантов ответа имеет 2 класса: {['secondary', 'section']}. Заменим этот код так, чтобы вместо 'secondary' была переменная, отвечающая за цвет.

{question.answers.map( answer => {
  let buttonClass = "secondary"
    if ( req.query && req.query.answered == answer ) {
      buttonClass = answer == question.correct ? 
      "success" : "danger"
    }
  return <button
    onClick={buttonHandler.apiCall({ value: answer })}
    class={[ buttonClass ,"section"]}
    title={answer}
    />
}
)}

Этот код выводит на экран кнопки. Та, на которую кликнул пользователь, будет окрашена: присвоен класс "success" если этот вариант ответа является правильным или класс "danger" если нет.

Итоговый код

❤️ Made with love on Chatium

ООО "Чатиум"

Информация о компании