Recoil : Efficient state management library for React

Boaz Hwang
Geek Culture
Published in
2 min readJul 19, 2021

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.

--

--