Dalej rozwijamy nasz mini-projekt, czyli aplikacja quizowa w biblioteczce React. Powoli zaczynamy robić kontekst z prawdziwego zdarzenia, z reducerem i własnymi hookami.

Ok tworzymy plik QuizContext.js. Będę pokazywał od dołu, bo wtedy lepiej zrozumiemy:

const initialState = {
    timedQuestions: false,
    timedQuiz: false,
    questionTime: 5,
    quizTime: 30,
    mode: 'menu',
    name: '',
    score: 0
};

Chyba wiadomo, o co chodzi. To jest initial state. Dobra, teraz reducer:

function quizContextReducer(state, action){
    switch (action.type) {
        case 'toggleTimedQuestions': {
          return {
            ...state, 
            timedQuestions: !state.timedQuestions
          };
        }
        case 'toggleTimedQuiz': {
            return {
              ...state, 
              timedQuiz: !state.timedQuiz
            };
          }

        case 'questionTimeChange': {
            return {
              ...state, 
              questionTime: action.payload
            };
          }
          case 'changeQuizTime': {
            return {
              ...state, 
              quizTime: action.payload
            };
          }

          case 'changeMode': {
            return {
              ...state, 
              mode: action.payload
            };
          }

          case 'changeName': {
            return {
              ...state, 
              name: action.payload
            };
          }

          case 'updateScore': {
            return {
              ...state, 
              score: state.score + action.payload
            };
          }
          case 'restart': {
            return {
              ...state, 
              score: 0,
              mode: 'menu'
            };
          }
        
        default: {
          throw Error('Unknown action: ' + action.type);
        }
      }
    }

Ogarnijmy sobie, co tu się dzieje, zwłaszcza, że już tego typu rzeczy omawialiśmy. Ok, zaczynamy robić funkcje eksportowe:

 export function useQuiz() {
    return useContext(QuizContext);
  }
  
  export function useQuizDispatch() {
    return useContext(QuizDispatchContext);
  }

Czyli tak, jedna jest do odczytywania, druga do dispatchowania akcji. To wszystko jest w dokumentacji Reacta, zalecany przez nich sposób skalowania kontekstu.

Dobra, teraz cały bajer, czyli komponent:

import { createContext, useContext, useReducer } from 'react';

const QuizContext = createContext(null);

const QuizDispatchContext = createContext(null);

export function QuizProvider({ children }) {
    const [state, dispatch] = useReducer(quizContextReducer, initialState);
  
    return (
      <QuizContext.Provider value={state}>
        <QuizDispatchContext.Provider value={dispatch}>
          {children}
        </QuizDispatchContext.Provider>
      </QuizContext.Provider>
    );
  }

Jeżeli wiemy, jak działa children oraz kontekst jako taki, to jesteśmy w domu. Tutaj mamy rozbicie wartości kontekstu oraz dispatchera na dwa providery i to jest dobre.

Nie chcę przytłoczyć zbyt wieloma rzeczami w jednej lekcji, więc zobaczymy sobie jak teraz działa nasz komponent App i resztę dopiszemy w następnej:

import './App.css';
import { QuizProvider } from './QuizContext';
import { Mainloop } from './Mainloop';

function App() {

  return (
    <QuizProvider>
        <Mainloop/>
    </QuizProvider>
  )
  
}

export default App;

Piękne. Ok, a teraz takie pytanko, czy ja ten mainloop zrobiłem z powodów czysto estetycznych? Dodam, że mainloop wykorzystuje useQuiz. To jak? Można użyć useQuiz w App?

Jak się zapewne domyślamy, nie można. Bo App nie jest oplecione QuizProviderem, tylko jego children. Dopiero dzieci tego providera mogą robić useContext na ten kontekst.

Ok, nie chcę przytłaczać, więc część dalsza będzie w następnej lekcji.