Wprowadzamy do naszej mini-aplikacji bardzo, bardzo prosty context. Uczymy się go używać, później rozbudujemy. Do dzieła.

Ok, tworzymy plik z kontekstem:

import { createContext} from "react";

const TimedQuestionsContext = createContext(null);

export default TimedQuestionsContext;

Teraz zmieniamy App RFC:

import './App.css';
import { useState} from "react";
import { Menu } from './Menu';
import { Quiz } from './Quiz';
import { Finish } from './Finish';
import TimedQuestionsContext from './TimedQuestionsContext';

function App() {
  
  let [mode, setMode] = useState('menu');
  let [name, setName] = useState('');
  let [score, setScore] = useState(0);
  let [timed, setTimed] = useState(false);

  return (
    <div className="App">
      <TimedQuestionsContext.Provider value={{timed, setTimed}}>
        {mode === 'menu' && <Menu setMode={setMode} name={name} setName={setName}/> }
        {mode === 'quiz' && <Quiz name={name} setMode={setMode} score={score} setScore={setScore}/> }
        {mode === 'finish' && <Finish setMode={setMode} name={name} score={score} setScore={setScore} />}
      </TimedQuestionsContext.Provider>
    </div>
  )
  
}

export default App;

Dokładnie to zmieniliśmy:

//dodaliśmy import
import TimedQuestionsContext from './TimedQuestionsContext';
//nowy stan
let [timed, setTimed] = useState(false);
//provider
 <TimedQuestionsContext.Provider value={{timed, setTimed}}>
        //(...)
 </TimedQuestionsContext.Provider>

Teraz w menu RFC tego sobie użyjemy:

import { useRef, useEffect, useContext } from 'react';
import TimedQuestionsContext from './TimedQuestionsContext';
function Menu({setMode, name, setName}){

    const {timed, setTimed} = useContext(TimedQuestionsContext);

    const btnRef = useRef(null);

    useEffect(() => {
        if(name === ""){
            btnRef.current.setAttribute("disabled", "");
        } else {
            btnRef.current.removeAttribute("disabled");
        }
      }, [name]);

    return (
        <>
        <p>Your name: {name}</p>
        <input type="checkbox" id="timedQ" onChange={() => setTimed((p) => !p)}  />
        <label for="timedQ">Timed Questions: {timed ? 'YES' : 'NO'}</label>
        <p></p>
        <input type="text" onChange={(e) => setName(e.target.value)} value={name}/>
        <button ref={btnRef} onClick={() => setMode("quiz")}>Start Quiz</button>
        </>
    )
};

export {Menu};

Dokładnie to zmieniliśmy:

//import useContext i kontekstu

import { useRef, useEffect, useContext } from 'react';
import TimedQuestionsContext from './TimedQuestionsContext';

//useContext
const {timed, setTimed} = useContext(TimedQuestionsContext);

//checkbox dodany
<input type="checkbox" id="timedQ" onChange={() => setTimed((p) => !p)}  />
<label for="timedQ">Timed Questions: {timed ? 'YES' : 'NO'}</label>
<p></p>

Jakby kogoś interesował pusty paragraf – cóż, CSS jeszcze nie gotowy, a to sposób, aby zrobić sobie newline.

Ok, znamy schemat, to Question RFC już pokażę w całości:

import QUESTIONS from './questions.js';
import { Answers } from './Answers.js';
import Timer from './Timer.js';
import { useContext } from 'react';
import TimedQuestionsContext from './TimedQuestionsContext.js';

function Question({index, handleAnswer, timerKey}){
    let timer = 5000;
    const {timed} = useContext(TimedQuestionsContext);
    return (
        <>
        {timed && <Timer key={timerKey} timeout={timer} onTimeout={() => handleAnswer(null)}/> }
        <h2>{QUESTIONS[index].text}</h2>
        <Answers answers={QUESTIONS[index].answers} handleAnswer={handleAnswer}/>
        <button onClick={() => handleAnswer(null)}>Skip Answer</button>
        </>
    )
};

export {Question};

I to jest fajne w React, zrobiliśmy sobie komponent, który ma swoją logikę i wygląd i wystarczy tylko go nie wyświetlić albo wyświetlić, aby całość działała w ten czy inny sposób bez ceregieli.

Dokładnie, chodzi mi o to:

{timed && <Timer key={timerKey} timeout={timer} onTimeout={() => handleAnswer(null)}/> }

Sobie sprawdzamy, czy timed, jeśli tak, to wyświetlamy komponent i już. Z dobrze napisaną logiką możemy potem tak sobie kod układać.

Może nam się wydawać, że to wszystko, ale reset jeszcze jest potrzebny:

import TimedQuestionsContext from './TimedQuestionsContext';
import { useContext } from 'react';
function Finish({setMode, name, score, setScore}){
    const {setTimed} = useContext(TimedQuestionsContext);
    function handleRestart(){
        setScore(0);
        setMode('menu');
        setTimed(false);
    }
    return (
        <>
        <p>Quiz finished!</p>
        <p>Your name: {name}</p>
        <p>Your score: {score}</p>
        <button onClick={handleRestart}>Restart Quiz</button>
        </>
    )
};

export {Finish};

Względnie możemy to olać, ale niech w menu checkbox odzwierciedla timed:

<input type="checkbox" id="timedQ" checked={timed ? true: false} onChange={() => setTimed((p) => !p)}  />

Ok, to by było na tyle, więcej Reacta niedługo!