2nd Homework
Goal of this homework is create new page in React, connect it to GraphQL, and make it interactive while using clean code practices.
You can see example of working implementation here.
There are 6 steps:
- Create initial version of
Practical03page - Display list of quacks
- Add "Select" functionality
- Clean up code (atoms, molecules, templates)
- Add auto-refetch functionality
- Send a link to working app page
Deadline: Monday 19/10/2020 - 23:59:59
If you have any problems contact your teacher using MS Teams: Tomas Horacek (@hort19).
Step 1: Create Initial Version of Practical03 Page
- connect to server using Atom (or your other editor, if you set it up for remote SFTP access)
- if you have trouble connecting see "Hello, Server!" guide or contact your teacher
Create Practical03.js
- open folder
frontend/src/pages - add new file named
Practical03.js- (you can right-click on
pagesfolder to add file)
- (you can right-click on
- copy&paste this as a content of this file:
Update Routes.js
- open file
frontend/src/Routes.js - add import:
- add
Practical03toPRACTICALSarray:
Test That it is Working
- open http://dev.frontend.
USERNAME.vse.handson.pro/practical/03- (change
USERNAMEto your SSH username)
- (change
- you should see:
- green top navigation
- content of page should be just:
Practical 03
- check current state of this page as you change/add code
- if there are any errors try to fix them before you procede further
Step 2: Display List of Quacks
Handle Special States
Error and loading states are sometimes called "special states".
- open
frontend/src/pages/Practical03.jsin Atom - edit body of
Practical03component so it will:- display
<div>Loading...</div>ifquacksState.loadingistrue - display "error message" if
quacksState.erroris notundefinedif (quacksState.error)will do the job of deciding iferrorisundefined- to display "error message" you can use
quacksState.error.message
- hint: use
console.log('quacksState:', quacksState)to see what attributes you can use and how values changes - hint: both special states could use this pattern:
- display
- you can test
- loading: by going to different page and back
- error: by editing
QUACKS_QUERYso it contains property that does not exist (e.g.xtextinstead oftext)
Display Quack Texts
- once "special states" are handled you can use
quacksState.datato display list of quacks - use
console.log('data:', quacksState.data)to see what is inside quacksState.data.quacksis array of quacks- to display it for the useser you can use
mapmethod:
- now you should see list of quack texts on the page
Use Snippet To Display Quacks
Replace this:
with this:
- replace
Quack text here!with{quack.text} - you should see styled quacks texts
Connect name and userName in Quack
- edit the query:
- so for each quack you will get:
text(already there)iduser- in
userask fornameanduserName
- in
- you can use GraphQL Playground to test your query:
- http://dev.backend.
USERNAME.vse.handson.pro/graphql - (change
USERNAME)
- http://dev.backend.
- connect
quack.user.nameandquack.user.userNameso it is visible onPractical03page
Step 3: Add "Select" Functionality
Edit Practical03 component so it support selecting one quack.
You can see example of working implementation here by clicking on Select buttons.
Add useState
- add
useStateafteruseQuery:
selectedQuackIdwill track whichquack.idis selected- if
selectedQuackId === quack.idit means that quack is selected
- if
- add
onClickprop to<Button>Select</Button>- it should be
onClick={() => setSelectedQuackId(quack.id)}
- it should be
Display Selection
- this div will determin if cell is "selected":
<div className="mv2 pa2 flex br2 bg-black-05">- for unselected state use CSS class
bg-black-05 - for selected state use CSS class
bg-light-green
- for unselected state use CSS class
- you have to change change how
classNameprop for thisdivworks - hints:
- you should use
classNames()function - because
bg-black-05andbg-light-greenhave-in their names you have to put them in quotes to make them work in objects:
- you should use
You have to replace false and true in code above with:
selectedQuackId === quack.id- and:
selectedQuackId !== quack.id
Now you should be able to click on different quacks and select them
Step 4: Clean up Code (Atoms, Molecules, Templates)
Now let's clean up Practical03.js in four steps:
- create
QuackTextatom - create
SelectableQuackWrapperatom - create
SelectableQuackmolecule - create
Practical03Templatetemplate
Step 4.1: Create QuackText Atom
- go to
frontend/src/atomsand createQuackText.js - content of this file should be:
children is a special prop. Anything that you wrap inside <QuackText>...</QuackText> will be passed to QuackText in this children prop.
- open
frontend/src/atoms/index.js- add there
export { QuackText } from './QuackText';
- add there
- this "re-export" in
index.jsfile will allow us to use:instead of:
Use QuackText in Practical03.js
- go to
frontend/src/pages/Practical03.js - add
QuackTextto imports from'src/atoms/':
In Practical03 component replace this:
with this:
Step 4.2: Create SelectableQuackWrapper Atom
Repeat the same steps as in QuackText for SelectableQuackWrapper.
Replace the selectable <div> (that one with changing background color) with new atom SelectableQuackWrapper:
- create
SelectableQuackWrapper.jsinfrontend/src/atoms- props for this component should be
childrenandisSelected: - copy the
<div className=...>of selectable div fromPractical03toSelectableQuackWrapper - update the code in
SelectableQuackWrapperso it usesisSelectedandchildren - remember to add imports in
SelectableQuackWrapper.js:
- props for this component should be
- re-export the atom component in
src/atoms/index.js - import
SelectableQuackWrapperinPractical03 - replace old selectable
<div>with new atom: - remember to update also closing tag:
- replace
</div>with</SelectableQuackWrapper>
- replace
It should look like this:
Step 4.3: Create SelectableQuack Molecule
Repeat similar steps for SelectableQuack. Difference is that this time you are creating molecule inside molecules folder.
- go to
frontend/src/molecules - create new file
SelectableQuack.js - move there everything that is inside
<SelectableQuackWrapper>...</SelectableQuackWrapper>(including<SelectableQuackWrapper>) to newSelectableQuackcomponent - it should look like this:
- add props that you will need here and use them in this component
- (e.g.
quack,isSelected,onSelect)
- (e.g.
- add import of
React - add imports of atoms (you can copy&paste the import from
Practical03.js) - re-export
SelectableQuackinfrom/src/molecules/index.js
Open frontend/src/pages/Practical03.js:
- add import of
SelectableQuack:
- use
SelectableQuackto display quacks, e.g.:
- remember to use
keywhen you are using.map!
Step 4.4: Create Practical03Template Template
- go to
frontend/src/templatesand createPractical03Template.js - move code for displaying loading, error and quacks from
Practical03toPractical03Template - it should use props:
quacksStateselectedQuackIdsetSelectedQuackId
- remember to add required imports for
Practical03Template.js
Open Practical03.js and import Practical03Template:
Use Practical03Template in Practical03 component
- result code should look like:
- remove unused in
Practical03.js(e.g. atoms, molecules,classNames, ...)
Good job!
Code should now look clean!
Step 5: Add Auto-Refetch Functionality
- this step is optional
- you can procede to 'Step 6' and skip this one
- but what you learn here will help you later in project
Refetch GraphQL Query
You can call quacksState.refetch() to re-fetch data from backend.
It can be used for example like this:
We will use it for refetching data every 5 seconds after data finished fetching.
useEffect
For this we have to know when data finished loading.
And we can use useEffect hook for this:
useEffect takes two arguments:
dependenciesis array of values, that will be watched for changes- if any of those values changes it will call the
callback callbackis called only when the change occurres- if there is no change in
dependenciesit will not call thecallback
- if there is no change in
So useEffect(callback, [quacksState.loading]) means that callback will be called when there is and update to quacksState.loading
- this means that previous
quacksState.loadingwastrueand now it'sfalse(or vice versa)
If we put it together:
This means that alert() called whenever loading is changed to false.
setTimeout
We can use JavaScript global function setTimeout to call some code after delay time.
It takes two arguments:
delayis number of miliseconds to waitcallbackwill be called when wait time passed
This code:
will call quacksState.refetch() after 5 seconds.
clearTimeout
clearTimeout is global JavaScript function that will cancel previously created timeout.
This is good, bucause user can leave the page before those 5 seconds will pass.
If we would not cancel the timeout it would call quacksState.refetch() for page that does not exist.
And this is bad!
We can use clearTimeout like this:
useEffect cleanup callback
useEffect hook have support for creating cleanup callbacks.
In simple case the "cleanup callback" is called when component unmounts (e.g. user navigates to different page).
There is a bit more to "cleanup callback", but for now you can think about it this way:
- when you create something in
useEffect(e.g. timeout usingsetTimeout) - you have to clean it up in "cleanup callback" (e.g. call
clearTimeout)
Correct usage of setTimeout in React would be this:
Put it All Together
Use useEffect to:
- watch when
quacksState.loadingchanges - when
quacksState.loadingisfalse:- create new timeout that will call
quacksState.refetch()with delay of5000miliseconds - remember to create "cleanup callbacks" that cancels timeout when page unmounts
- create new timeout that will call
Step 6: Send a Link to Working App Page
Once you are finished:
- send a link to working app page
http://dev.frontend.USERNAME.vse.handson.pro/practical/03 - using MS Teams to: Tomas Horacek (@hort19)
Deadline: Monday 19/10/2020 - 23:59:59