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!