- Display random emoji in
App
component- Import array of emojis from
src/assets/emojis.js
to component file - Display first emoji from the array (codepoint and emoji itself)
- In
render
get random value of index and display emoji from that index - Extract index value to component state
- Display button with text
Get another emoji
- Implement function calculating another random index value and saving it in state
- Connect function for calculating random index with button click
- Use fat arrow or bind
this
in order to preventthis
being undefined
- Use fat arrow or bind
- Import array of emojis from
- Use
String.fromCodePoint
function to parse codepoints into emoji and display parsed emoji - Tweak algorithm for getting random index to return values bigger than array of emojis
- When no emoji is selected, display alternative text (use ternary operator for conditional rendering)
- Extract emoji rendering markup to component method
- Use twemoji package to handle emojis not supported (rendered as square)
- https://www.npmjs.com/package/twemoji
- add
twemoji
package via npm import twemoji from 'twemoji'
- follow the package documentation
- Extract emoji rendering markup to separate component
src/components/DisplayEmoji.js
- Use functional component (just a fat arrow returning markup)
- accept
props
as parameter to this function
- accept
- import extracted component in main one and render it
- Pass selected index to
DisplayEmoji
as prop - In
DisplayEmoji
replace usages of this.state.index with props.index
- Use functional component (just a fat arrow returning markup)
- Add
proptypes
package via npm- Import PropTypes and define them for
DisplayEmoji
- Import PropTypes and define them for
- Implement resetting index in
DisplayEmoji
component- Create method
resetIndex
inApp
resettingstate.index
to0
- Pass this method to
DisplayEmoji
asresetFn
property (the same way index property is passed) - Add button in
DisplayEmoji
component - Connect
resetFn
method to button viaonClick
property (that way child can change parent data - data goes down, function calls go up - unidirectional data flow)
- Create method
- In
DisplayEmoji
add counter for how long is emoji visible- Migrate
DisplayEmoji
from functional to class-ical component type. - Add
secondsPassed
toDisplayEmoji
component state with initial value 0 - Use
componentDidMount
react lifecycle method forDisplayEmoji
andsetInterval
js function to incrementthis.state.secondsPassed
every 1 second. - Fix counter not resetting on selecting new emoji by providing unique
key
property<DisplayEmoji key={this.state.index}>
- Fix errors about setting state of unmounted component by saving interval id to
DisplayEmoji
state and incomponentWillUnmount
lifecycle hook add pass interval id as argument to browser functionclearInterval
(setState is async, so it does not guarantee property in this.state.intervalId to be updated in the line below)
- Migrate
- Implement getting new emoji automatically after 10s
- Pass
getRandowEmoji
method as prop toDisplayEmoji
- When
secondsPassed
is 10, call method received from parent to get new emoji.
- Pass
- Display list of last 5 emojis
- Save 5 last displayed emojis (not indexes) in main component state.
- Below currently selected emoji, map over array of stored previous emojis and reuse
DisplayEmoji
component for every one. (key property needs to be unique only for the siblings)
- (extra) Add input for getting time when new emoji should be selected
- Add html input
- (1 way) Connect input to react as uncontrolled component - use
onBlur
handler andref
property. Blur handler should get value from the input available via ref property.
- (extra) ReactDevTools browser add-on
-
Extract behaviour functionality from DisplayEmoji into HOC (Higher Order Component)
- Create new file
withTimer.js
insrc/assets
folder- Create and export fat arrow function
withTimer
which receivesWrappedComponent
property - Make function
withTimer
return class componentWithTimer
(import React, Component, PropTypes) - Copy all methods handling secondsPassed state into
WithTimer
and remove them fromDisplayEmoji
- Rename
getNewEmoji
toresetHandler
and update name of this prop being send inApp
- Rename
resetFn
property andresetIndex
method toclearIndex
wherever they are used, to prevent confusion withresetHandler
property - Define
propTypes
forWithTimer
class before returning it fromwithTimer
- Create and export fat arrow function
- In
DisplayEmoji
change bindingthis.state.secondsPassed
intosecondsPassed
and destructure it from props (just like index and resetFn properties)- Update
propTypes
forDisplayEmoji
to contain index, resetFn and secondsPassed properties
- Update
- Display div with
secondsPassed
conditionally when it is>= 0
to handle situations when this property is not passed. - Change DisplayEmoji back to functional component
- Create new file
src/assets/NoEmojiMessage.js
- Extract markup rendering info about no emoji for given index to functional component to file
NoEmojiMessage.js
- functional component should accept index property
- change
this.state.index
to justindex
received as argument - add conditional displaying of 'secondsPassed' just like in DisplayEmoji and accept it as argument
- Extract markup rendering info about no emoji for given index to functional component to file
- Create new file
-
Wrap both components in HOCs and use them in
App
- In
NoEmojiMessage.js
- Import
withTimer
HOC - Add default export for
withTimer(NoEmojiMessage)
and use it inApp.js
- Add
resetTime
andresetHandler
andkey
properties to NoEmojiMessage component inApp.js
- Import
- In
DisplayEmoji.js
- Import
withTimer
HOC - Add default export for
withTimer(DisplayEmoji)
and use it inApp
- Import
- In
-
When rendering 5 last emojis use
DisplayEmoji
component without HOC as they do not need timer functionality- Make
clearIndex
property optional and render button only when it is provided - Update
state.previousEmojis
to contain indexes not whole Emojis (update state initialization andgetRandomEmoji
) - Replace div rendering emoji in function mapping through
previousEmojis
withDisplayEmoji
orNoEmojiMessage
depending on index value being less thanEmojis.length
- Make
-
Add input for getting time when new emoji should be selected
- (2 way) Change input to controlled component - use
value
andonChange
handler. onChange handler should callsetState
saving new value received as parameter. - (Using this pattern you can prevent updating input value/reformat it which is useful when having inputs for area code or phone number)
- (2 way) Change input to controlled component - use
- validation, and mocked server validation
- error handling
- formatting
- scoped fields (price range, disabling options depending on other input)
- array of fields (you can add/remove them)
- Add form data structure matching
Form schema
below toRegisterForm
state
-
age - presence, numericality - if below 13 hides form
-
username - presence length and regex (serverside already taken from the predefined list)
-
email - presence regex (serverside already taken from the predefined list)
-
addresses[] - inputs not validated if every is empty - can add/remove from the list
- addresses[]city - presence presence in predefined list - it automatically fills zip-code
- addresses[]zipCode - presence presence in predefined list - it automatically fills city, auto formatting
-
Add
handleChange
method updating state property accessible byevent.target.name
with valueevent.taget.value
- create method
handleChange
- import
set
method fromlodash
package - set
value
usingname
path tothis.state.data
- setState with modified state assigned to
data
property
- create method
-
Add
age
input field and fill up the missing parts<div className={cn('form-group', { 'has-error': get(X, 'X') })}> <label htmlFor="X">X</label> <input type="text" className="form-control" name="X" placeholder="X" value={X} onChange={X} /> {get(X, 'X') && <span className="help-block">{X}</span> } </div>
- import cn from classnames package
-
Repeat for
userName email
fields -
Add
adresses
field lists- Add iteration over addresses in form data and render
<div className="panel panel-default" key={index}> <div className="panel-heading"> <h3 className="panel-title">address #{index+1}</h3> <button>Remove</button> </div> <div className="panel-body"> Panel content </div> </div>
- Create method removing address of given index from form data
- Bind Remove button to removing function
- Create method adding new address to form data
- After iteration add button triggering adding new address
- In place of
Panel content
addselect
element forcity
similar as before, but use nested name likeaddresses[0].city
where 0 is current index value- Inside
select
tag putoption
tags for some cities
- Inside
- Add regular input field for
zipCode
and bind it accordingly
-
Bind fields city and zipCode together
- Replace their city handleChange method with custom one
- In this custom method trigger handleChange of zipCode field with appropriate value
-
Add validations
- Make function
validate
return false if errors detected - Update
state.errors
with properly nested error objects according to specification - If no errors detected then return true
- Make function
-
(extra) fragments from https://www.youtube.com/watch?v=-tDy7ds0dag
-
(extra) bootstrap https://bootstrapdocs.com/v3.3.6/docs/css/#overview
-
(extra) yup https://github.com/jquense/yup
- Format zipCode field
- create method transforming zipCode in raw form into dashed form or in reverse depending on parameter
- bind transforming method with format flag to zipCode value
- before passing value to handleChange transform it with format flag off
- What is Jest
- What is Enzyme
- shallow vs mount
- lifecycleExperimental for shallow
- Types of testing approaches
- Unit test for method
- User interaction test for method
- Snapshot test for markup & markup changes
setup
pattern and the steps leading to it
- Things to test
- Markup and props (for snapshots)
- Regular component method
- Handler method triggered by user interaction
- Lifecycle method
- Asynchronous method
- Method which returns complex object (to use special matchers)
- Method conditionally calling function from props with attributes (to expect it being called with attributes)
- mock import
- snapshot specs
- markup in different conditions
- snapshot tests for data
- tests for implementation
- all lifecycle methods
- triggered naturally
- triggered manually
- handler methods via find and simulate event
onClick
onSubmit
event handlers
- all non handler methods in components (they are called by lifecycle, handlers, or child components)
- all lifecycle methods
- async testing
handleSubmitByBackend
inwithBackend
- useful matchers (objectContaining, arrayMatching)
- testing HOCs
- using mock component
- mocking imports
- mock
twemoji.parse
method inDisplayEmoji
- mock
emojis
import inRoulette
- mock
- coverage generation and usage
- What is Redux
- https://medium.freecodecamp.org/an-introduction-to-the-redux-first-routing-model-98926ebf53cb
- https://cdn-images-1.medium.com/max/2000/1*R_d_jeLBUp3hdjLeWRnz4Q.png
- When to use it
- Rules
- Do not mutate state
- Redux Devtools
- Reducers
- Action Creators
- Selectors
- setup redux and react-redux
- setup redux dev-tools
- implement registration via redux
- define which state properties are related to handling registration status
- create folder
src/store/registration
withtypes.js
,actionCreators.js
,reducer.js
,selectors.js
files - define and export
types
for what can happen with registration (login/logout) - implement reducer for registration which will react on the types https://redux.js.org/docs/basics/Reducers.html#handling-actions
- implement action creators for defined types https://redux.js.org/docs/basics/Actions.html#action-creators
connect
App
component to store and map state to its props https://redux.js.org/docs/basics/UsageWithReact.html#containersfilterlinkjsconnect
withBackend
WrappedComponent
to store and map action creators to its props https://redux.js.org/docs/basics/UsageWithReact.html#containersfilterlinkjs- remove state from
App
which was extracted to redux - remove props from
withBacked
which were reimplemented via actionCreators - update
withBackend
methods which previously called handleRegistration, to call proper action creator
- implement emojis via redux
- define which state properties are related to handling emoji
- create folder
src/store/emojis
withtypes.js
,actionCreators.js
,reducer.js
,selectors.js
files - define and export
types
for what can happen with emoji (initialization, selecting new) - implement reducer for emojis which will react on the types https://redux.js.org/docs/basics/Reducers.html#handling-actions
- implement action creators for defined types https://redux.js.org/docs/basics/Actions.html#action-creators
connect
Roulette to store and map state and action creators to its props https://redux.js.org/docs/basics/UsageWithReact.html#containersfilterlinkjs- create selectors so component does not need to know about store structure https://redux.js.org/docs/recipes/ComputingDerivedData.html#containersvisibletodolistjs
- remove state from Roulette which was extracted to redux
- update Roulette methods which previously changed state, to call proper action creator
- Free Redux tutorial on egghead https://egghead.io/courses/getting-started-with-redux
- Part 2 Redux tutorial on egghead https://egghead.io/courses/building-react-applications-with-idiomatic-redux
Done during CoderDojo at 22-Feb-2018 - LocalStorage, Redux-Thunk middleware and fetch app internal data
- Add redux thunk package
- Resolve warning
"react-test-renderer@15.6.2" has incorrect peer dependency "react@^15.6.2"
if occurs - Store
isRegistered
flag inlocalStorage
- Add
src/utils/localStorage
withload
andsave
registrationStatus methods - For loading isRegistered flag use
localStorage.getItem
method - Add second parameter to
createStore
which matches the store structure and sets isRegistered to result ofloadRegistrationStatus
method - Change
registration/actionCreators
to use thunk, so they callsaveRegistationStatus
setting proper flag - Add logout button and connect it to proper action creator (it should be wrapped in div.logout to have minimal styling)
- Add
- Add json-server to store data there
- Migrate Emojis fetching to thunk
- On
setupEmojis
action creator, call json-server using fetch to get emojis and store them - Migrate emojis identification to id's
- Update algorithm for getting random emojis to use ids
- Update reducer, selectors, components
- On
- Migrate user login to thunk
- Make a call to the json-server to persist user provided data
- Save userId into
localStorage
-
Add jokes resource holding available jokes
- Add
store/jokes
folder withreducer
,actionCreators
- Add
-
Add Draw resource joining joke and emoji
- Add
store/draws
folder withreducer
,actionCreators
,types
,selectors
- Draw should have 3 properties
jokeId
,emojiId
,id
(generated withDate.now()
)
- Add
-
Implement
setNewDraw
instore/draws/actionCreators
fetching random joke and emoji- Create
getRandomJoke
instore/jokes/actionCreators
AC which calls Chuck Norris API and returns promise with it's response - Create
getRandomEmoji
instore/emojis/actionCreators
which draws random emoji ID and fetches given emoji from DB - Call
getRandomJoke
insetNewDraw
and get data from promise - Call
getRandomEmoji
insetNewDraw
and get data from promise - When both promises resolve build
draws/SET_NEW
action - Reducers for
draw
,emoji
andjoke
should handledraws/SET_NEW
action accordingly by saving pieces of data
- Create
- Implement saving history of last 5 draws in LocalStorage
- When adding new Draw, get currently stored history of draws and add new one use getState from redux-thunk
- Implement
utils/LocalStorage
methods to handle saving/loadingisRegistered
anddraws
- When user logs out
draws
should be cleared out
- When user logs out
- Save updated history in LocalStorage
draws
- Emit updated history in action payload
- Update
draw/reducer
handling to assign new draw to history
-
Implement initial Draw loading
- Implement
setupDraws
inRoulette#componentDidMount
- Add
setupDraws
action creator - Fetch draws history from LocalStorage
- For every jokeId call ChuckNorris API and fetch them
- For all emojis call backend and fetch all required emojis with one request
- When all promises resolve create
draws/SETUP
action - Reducers for
draw
,emoji
andjoke
should handledraws/SETUP
action accordingly by saving pieces of data
- Implement
-
Test action creators, reducers and selectors
- For emojis, jokes, draws and registration
- Form handling
- Image handling
- Overall design
- Coloring
- Sizes
- (extra) Use axios to make requests to external api