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
Practical03
page - 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
pages
folder 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
Practical03
toPRACTICALS
array:
Test That it is Working
- open http://dev.frontend.
USERNAME
.vse.handson.pro/practical/03- (change
USERNAME
to 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.js
in Atom - edit body of
Practical03
component so it will:- display
<div>Loading...</div>
ifquacksState.loading
istrue
- display "error message" if
quacksState.error
is notundefined
if (quacksState.error)
will do the job of deciding iferror
isundefined
- 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_QUERY
so it contains property that does not exist (e.g.xtext
instead oftext
)
Display Quack Texts
- once "special states" are handled you can use
quacksState.data
to display list of quacks - use
console.log('data:', quacksState.data)
to see what is inside quacksState.data.quacks
is array of quacks- to display it for the useser you can use
map
method:
- 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)id
user
- in
user
ask forname
anduserName
- 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.name
andquack.user.userName
so it is visible onPractical03
page
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
useState
afteruseQuery
:
selectedQuackId
will track whichquack.id
is selected- if
selectedQuackId === quack.id
it means that quack is selected
- if
- add
onClick
prop 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
className
prop for thisdiv
works - hints:
- you should use
classNames()
function - because
bg-black-05
andbg-light-green
have-
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
QuackText
atom - create
SelectableQuackWrapper
atom - create
SelectableQuack
molecule - create
Practical03Template
template
Step 4.1: Create QuackText
Atom
- go to
frontend/src/atoms
and 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.js
file will allow us to use:instead of:
Use QuackText
in Practical03.js
- go to
frontend/src/pages/Practical03.js
- add
QuackText
to 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.js
infrontend/src/atoms
- props for this component should be
children
andisSelected
: - copy the
<div className=...>
of selectable div fromPractical03
toSelectableQuackWrapper
- update the code in
SelectableQuackWrapper
so it usesisSelected
andchildren
- remember to add imports in
SelectableQuackWrapper.js
:
- props for this component should be
- re-export the atom component in
src/atoms/index.js
- import
SelectableQuackWrapper
inPractical03
- 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 newSelectableQuack
component - 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
SelectableQuack
infrom/src/molecules/index.js
Open frontend/src/pages/Practical03.js
:
- add import of
SelectableQuack
:
- use
SelectableQuack
to display quacks, e.g.:
- remember to use
key
when you are using.map
!
Step 4.4: Create Practical03Template
Template
- go to
frontend/src/templates
and createPractical03Template.js
- move code for displaying loading, error and quacks from
Practical03
toPractical03Template
- it should use props:
quacksState
selectedQuackId
setSelectedQuackId
- 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:
dependencies
is array of values, that will be watched for changes- if any of those values changes it will call the
callback
callback
is called only when the change occurres- if there is no change in
dependencies
it 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.loading
wastrue
and 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:
delay
is number of miliseconds to waitcallback
will 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.loading
changes - when
quacksState.loading
isfalse
:- create new timeout that will call
quacksState.refetch()
with delay of5000
miliseconds - 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