Recoil : Efficient state management library for React
The selector
about initialProps for quiz page also executes those. It has get
QueryData atom
and async API request to server for quiz data.
export default selector<TResponseData>({
key: 'initilaOrderState',
get: async ({ get }) => {
const queryData = get(QueryDataState);
if (queryData == undefined || window.location.pathname != `/${QUIZ_PAGENAME}`) return undefined;
const { amount, difficulty } = queryData;
const axios = customAxios();
const response = await axios({
method: 'GET',
params: {
amount,
difficulty,
type: 'multiple',
},
});
const decodedResponseData = {
...response.data,
...
};
return decodedResponseData;
},
});
After executing async request, selector
returns response data that is global state value. There is a component that has need to get this value(eg, Quiz component). And then, how render it?
Render component with Asynchronous data with React.suspense
Quiz component
in QuizPage gets async global state(initialProps) to render quiz data from server. So, It executes useRecoilValue
with InitialPropsState
.
import { useEffect, useRef, useState } from 'react';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import {
InitialPropsState,
CurrentQuizIndexState,
SelectedAnswerState,
QuizResultsState,
} from 'src/state';
const Quiz = () => {
const initialProps = useRecoilValue(InitialPropsState);
const currentQuizIndex = useRecoilValue(CurrentQuizIndexState);
const currentQuiz = (initialProps as TResponseData).results[currentQuizIndex];
...
return (
<Content
header={`QUIZ ${currentQuizIndex + 1} / ${(initialProps as TResponseData).results.length}`}
headerRight={currentQuiz.difficulty}>
<Atoms.Div ... >
<Atoms.Div>
<Atoms.Div fontSize="24px">{currentQuiz.question</Atoms.Div>
<Atoms.Ul>
{currentQuiz.examples.map((answer: string, i: number) => (<Atoms.Li key={answer} value={answer}>
<Atoms.Checkbox
id={answer}
name={answer}
disabled={selectedAnswer != undefined}
onChange={handleChange}
/>
<Atoms.Label
isSelected={selectedAnswer == answer}
isCorrect={selectedAnswer == currentQuiz.correct_answer}>{answer}</Atoms.Label>
</Atoms.Li>))}
</Atoms.Ul>
</Atoms.Div>
</Atoms.Div>
</Content>
);
};
export default Quiz;
And then, QuizPage component
renders Quiz component
import { Quiz, QuizFooter, QuizResult } from 'components/Organisms';
const QuizPage = () => {
return (
<>
<Quiz />
<QuizResult />
<QuizFooter />
</>
);
};
export default QuizPage;
Finally QuizPage component
is rendered in Router.tsx
with Suspense
.
import { Suspense } from 'react';
import { Helmet } from 'react-helmet';
import { Route, Switch } from 'react-router';
import { BrowserRouter } from 'react-router-dom';
import { QUIZ_PAGENAME, RESULT_PAGENAME } from 'src/constant';
import { ErrorBoundary, LandingPage, QuizPage, ResultsPage, ShimmerPage} from 'src/components/Pages';
const Router = () => {
return (
<BrowserRouter>
<ErrorBoundary>
<Suspense fallback={<ShimmerPage />}>
<Switch>
<Route path={`/${QUIZ_PAGENAME}`}>
<Helmet title="Quiz page" />
<QuizPage />
</Route>
...
</Switch>
</Suspense>
</ErrorBoundary>
</BrowserRouter>
);
};
export default Router;
Suspense
is a component that has children components that gets global state with async data from server(eg, Quiz component
with initialPropsState
). It also has props named fallback
that has to be assigned a component. It is rendered when async request starts in global state of children components, its state will be loading
. After executing async request, the state is hasValue
, the children components of Suspense
was rendered.
If async request state is hasError
, ErrorBoundary
was rendered with it. Because it can handle error with getDerivedStateFromError
method.
And That’s it.
Conclusion
We could learn something from this posting such as
- Atom
- Selector
- How to render components with async data by React.suspense
Those are not only easy to learn but also makes your code concise. After using those, You can feel Recoil is a powerful state management library. So, try it and happy coding.