- Display random emoji in
Appcomponent- Import array of emojis from
src/assets/emojis.jsto component file - Display first emoji from the array (codepoint and emoji itself)
- In
renderget 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
thisin order to preventthisbeing undefined
- Use fat arrow or bind
- Import array of emojis from
- Use
String.fromCodePointfunction 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
twemojipackage 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
propsas parameter to this function
- accept
- import extracted component in main one and render it
- Pass selected index to
DisplayEmojias prop - In
DisplayEmojireplace usages of this.state.index with props.index
- Use functional component (just a fat arrow returning markup)
- Add
proptypespackage via npm- Import PropTypes and define them for
DisplayEmoji
- Import PropTypes and define them for
- Implement resetting index in
DisplayEmojicomponent- Create method
resetIndexinAppresettingstate.indexto0 - Pass this method to
DisplayEmojiasresetFnproperty (the same way index property is passed) - Add button in
DisplayEmojicomponent - Connect
resetFnmethod to button viaonClickproperty (that way child can change parent data - data goes down, function calls go up - unidirectional data flow)
- Create method
- In
DisplayEmojiadd counter for how long is emoji visible- Migrate
DisplayEmojifrom functional to class-ical component type. - Add
secondsPassedtoDisplayEmojicomponent state with initial value 0 - Use
componentDidMountreact lifecycle method forDisplayEmojiandsetIntervaljs function to incrementthis.state.secondsPassedevery 1 second. - Fix counter not resetting on selecting new emoji by providing unique
keyproperty<DisplayEmoji key={this.state.index}> - Fix errors about setting state of unmounted component by saving interval id to
DisplayEmojistate and incomponentWillUnmountlifecycle 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
getRandowEmojimethod as prop toDisplayEmoji - When
secondsPassedis 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
DisplayEmojicomponent 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
onBlurhandler andrefproperty. 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.jsinsrc/assetsfolder- Create and export fat arrow function
withTimerwhich receivesWrappedComponentproperty - Make function
withTimerreturn class componentWithTimer(import React, Component, PropTypes) - Copy all methods handling secondsPassed state into
WithTimerand remove them fromDisplayEmoji - Rename
getNewEmojitoresetHandlerand update name of this prop being send inApp - Rename
resetFnproperty andresetIndexmethod toclearIndexwherever they are used, to prevent confusion withresetHandlerproperty - Define
propTypesforWithTimerclass before returning it fromwithTimer
- Create and export fat arrow function
- In
DisplayEmojichange bindingthis.state.secondsPassedintosecondsPassedand destructure it from props (just like index and resetFn properties)- Update
propTypesforDisplayEmojito contain index, resetFn and secondsPassed properties
- Update
- Display div with
secondsPassedconditionally when it is>= 0to 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.indexto justindexreceived 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
withTimerHOC - Add default export for
withTimer(NoEmojiMessage)and use it inApp.js - Add
resetTimeandresetHandlerandkeyproperties to NoEmojiMessage component inApp.js
- Import
- In
DisplayEmoji.js- Import
withTimerHOC - Add default export for
withTimer(DisplayEmoji)and use it inApp
- Import
- In
-
When rendering 5 last emojis use
DisplayEmojicomponent without HOC as they do not need timer functionality- Make
clearIndexproperty optional and render button only when it is provided - Update
state.previousEmojisto contain indexes not whole Emojis (update state initialization andgetRandomEmoji) - Replace div rendering emoji in function mapping through
previousEmojiswithDisplayEmojiorNoEmojiMessagedepending 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
valueandonChangehandler. onChange handler should callsetStatesaving 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 schemabelow toRegisterFormstate
-
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
handleChangemethod updating state property accessible byevent.target.namewith valueevent.taget.value- create method
handleChange - import
setmethod fromlodashpackage - set
valueusingnamepath tothis.state.data - setState with modified state assigned to
dataproperty
- create method
-
Add
ageinput 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 emailfields -
Add
adressesfield 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 contentaddselectelement forcitysimilar as before, but use nested name likeaddresses[0].citywhere 0 is current index value- Inside
selecttag putoptiontags for some cities
- Inside
- Add regular input field for
zipCodeand 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
validatereturn false if errors detected - Update
state.errorswith 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
setuppattern 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
onClickonSubmitevent handlers
- all non handler methods in components (they are called by lifecycle, handlers, or child components)
- all lifecycle methods
- async testing
handleSubmitByBackendinwithBackend
- useful matchers (objectContaining, arrayMatching)
- testing HOCs
- using mock component
- mocking imports
- mock
twemoji.parsemethod inDisplayEmoji - mock
emojisimport 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/registrationwithtypes.js,actionCreators.js,reducer.js,selectors.jsfiles - define and export
typesfor 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
connectAppcomponent to store and map state to its props https://redux.js.org/docs/basics/UsageWithReact.html#containersfilterlinkjsconnectwithBackendWrappedComponentto store and map action creators to its props https://redux.js.org/docs/basics/UsageWithReact.html#containersfilterlinkjs- remove state from
Appwhich was extracted to redux - remove props from
withBackedwhich were reimplemented via actionCreators - update
withBackendmethods 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/emojiswithtypes.js,actionCreators.js,reducer.js,selectors.jsfiles - define and export
typesfor 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
connectRoulette 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
isRegisteredflag inlocalStorage- Add
src/utils/localStoragewithloadandsaveregistrationStatus methods - For loading isRegistered flag use
localStorage.getItemmethod - Add second parameter to
createStorewhich matches the store structure and sets isRegistered to result ofloadRegistrationStatusmethod - Change
registration/actionCreatorsto use thunk, so they callsaveRegistationStatussetting 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
setupEmojisaction 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/jokesfolder withreducer,actionCreators
- Add
-
Add Draw resource joining joke and emoji
- Add
store/drawsfolder withreducer,actionCreators,types,selectors - Draw should have 3 properties
jokeId,emojiId,id(generated withDate.now())
- Add
-
Implement
setNewDrawinstore/draws/actionCreatorsfetching random joke and emoji- Create
getRandomJokeinstore/jokes/actionCreatorsAC which calls Chuck Norris API and returns promise with it's response - Create
getRandomEmojiinstore/emojis/actionCreatorswhich draws random emoji ID and fetches given emoji from DB - Call
getRandomJokeinsetNewDrawand get data from promise - Call
getRandomEmojiinsetNewDrawand get data from promise - When both promises resolve build
draws/SET_NEWaction - Reducers for
draw,emojiandjokeshould handledraws/SET_NEWaction 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/LocalStoragemethods to handle saving/loadingisRegisteredanddraws- When user logs out
drawsshould be cleared out
- When user logs out
- Save updated history in LocalStorage
draws - Emit updated history in action payload
- Update
draw/reducerhandling to assign new draw to history
-
Implement initial Draw loading
- Implement
setupDrawsinRoulette#componentDidMount - Add
setupDrawsaction 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/SETUPaction - Reducers for
draw,emojiandjokeshould handledraws/SETUPaction 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