Building Apps with React, Redux, and ES6 (by Cory House from Pluralsight) (in progress!)
- Cory House @housecor
- My Code from the course https://github.com/ltfschoen/react-redux
Table of Contents
Chapter 1 - Setup
Links to Code Starter Kits
- Pluralsight Redux Starter - Simplified version of React Slingshot
- React Slingshot - Redux, React, Babel, ES6, Hot Reload starter kit
Steps to Create Build (NPM, Webpack, ESLint, Babel, ES6, Express, Mocha)
- Added package.json using template from pluralsight-redux-starter
- Added index.html using template from pluralsight-redux-starter
- Added index.js
- Added webpack.config.dev.js using template from pluralsight-redux-starter
- Added .editorconfig using template above http://editorconfig.org/
- Added .babelrc using template above for Babel transpiler with ES6 to support all recent browsers
- Added .eslintrc using template above
- Add tools/srcServer.js to serve up files in src directory using Express
- Use NPM to create script to start Express server. Use babel-node to compile ES6 in srcServer
"start": "babel-node tools/srcServer.js"
and run withnpm start
and go to localhost:3000, and check Console for console.log’s and that bundle.js compiled - Add NPM
prestart
script in package.json. Disable ESLint use of console/* eslint-disable no-console */
- Add NPM
lint
script to run ESLint. ESLint does not support watch, so ESLint Watchesw
is used. - Add NPM
lint:watch
script to run ESLint Watch - Add NPM
test:watch
script to run Mocha Watch - Use
npm run all
to run multiple NPM scripts in sequence or parallel and output to command line
Steps to Create App (with React and React Router)
- Create pages using templates from react-slingshot
- Create folders for each Component within src/components
- Create HomePage.js and AboutPage.js Components (use convention of __Page.js suffix for top-level Components)
- Create layout
- Create Common Parent AppComponent src/components/App.js for markup shown on all pages and to wrap other components
- Configure routing
- Configure Entrypoint to use React Router in index.js
- Setup CSS
- Setup navigation
- Refactor Header into common Header Component
- Create skills Component and update routes and header
- Update skills Component with a
constructor
to initialisestate
for form and add markup withinput
fields - Add Event functions for form input change events (i.e.
onTitleChange
), that acceptsevent
parameter, extracts the target value and sets StatesetState
- Test Event functionality in browser and check console for errors
- Add Binding for ES6 Classes (since NOT done automatically) to overcome errors like
Cannot read property 'state' of undefined
by ensuring thethis
context is correct in theonTiteChange
handler, as the function inherits thethis
context of the caller (i.e. passing the input fieldthis
context to theonTiteChange
change handler) instead ofthis
being bound to the instance of the Component. Bind to thethis
context of the Component instance in theconstructor
for all Event handling functions. Note: Alternative is to implement the binding in therender
function, but binding on each render causes a new function to be run on each render, which negatively impacts performance (i.e. DO NOT<input onChange={this.onTitleChange.bind(this)}
) - Setup Actions for Redux
- Create Action Creator for skill in new folder/file of /src/actions/skillActions.js
- Define Actions (define convenience function that returns Action plain object that must have Type property)
- Refactor to use a
const
for the value of Type
Note: Using “short-hand property names”, the below are equivalent in ES6 (i.e. do not need to show value if key/value are the same)
- Setup Reducer
- Create Reducer for skill in new folder/file of /src/reducers/skillReducer.js, and set initial state
in default parameters
state = []
(explicit nameskillReducer.js
so easily identify the file in ‘tabs’ of IDE) - In the Reducer, each Reducer processes specific slice of State.
Fork logic based on the Type of Action being processed using
switch
(alternatively useif
, a lookup table of functions - DO NOT mutate State in the Reducer. Avoid mutating state by using ES6 spread operator to return new instance of State array, then create deep copy of the passed in action.skill and return a brand new State containing extra value of new action.skill
- Create Reducer for skill in new folder/file of /src/reducers/skillReducer.js, and set initial state
in default parameters
- Cont’d
- Note: In Flux we handled Actions in the Store. In Redux we handle Actions in Reducers (accepts State/Action and returns new State)
- Setup Root Reducer (to support using Multiple Reducers in Redux)
- Create folder/file at /src/reducers/index.js
- Alias specific Reducer name for use throughout app (when
import
ing)
- Setup Redux Store
- Create folder/file at /src/store/configureStore.js
- Define a function to configure the Store and initialise it using
initialState
with some State (good for server-side rendering), to be called at app entry point, so runs when start app - Pass parameters to
createStore
- Pass optional 3rd parameter as Middleware to enhance the Store using Redux function
applyMiddleware
and pass it any middleware to use in app (reduxImmutableStateInvariant
) and invoke it- Other middleware options are support for hot reloading, dev tools in chrome, etc (see react-slingshot repo)
- Update app entrypoint src/index.js to create instance of the Store Configuration.
- Optionally pass initial State to Reducers to override default State defined there Use Cases for passing initial State: To Rehydrate Store using separate State passed down from server-side or from local storage
- Wrap app with Redux
Provider
from react-redux Library to makes use of Redux Store Instance across React Container Components- Note:
Provider
is a higher-order Component that attaches Redux Store with React Container Components by wrapping over entire app so the Store may be accessed from React Container Components. It takes thestore
as a Property - In src/index.js, wrap the Router Component with the Provider Component
- Note:
- Update React Container Components by Decorating them with the React Redux
Connect
Component function- Note:
Connect
is a higher-order Component function that connects React Container Components with Redux - Note:
Connect
takes two functions as paramsmapStateToProps
andmapDispatchToProps
(may be named differently if desired) - Declare
mapStateToProps
- return Properties we want exposed/inject on the React Container Component- Declare Props to expose on Component so access with
this.props.__
. Obtain the data for Prop from within Redux Store using thestate
parameter (i.e.state.skills
would access the Aliasskills
for the skills Reducer defined in the Root Reducer) ownProps
allows access to Props attached to the React Component.
- Declare Props to expose on Component so access with
- Declare
mapDispatchToProps
(OPTIONAL) - Actions to expose on React Container Component as Props (i.e.createSkill
injected in Component by Connect function)- Approach Option #1 (Dispatch to Props) If we DELETE this optional
connect
parameter, doing so causes the Component to automatically get adispatch
Prop attached to it that is injected byconnect
, so now in therender
function can usethis.props.dispatch
- Use
dispatch
by importing reference to Action Creators, and then dispatching actions in the Event handlers of the Component by callingthis.props.dispatch(skillActions.createSkill(this.state.skill))
and passing it an Action and Data for handling by Redux - Note:
dispatch
is a function that allows firing off Actions that are defined in the Action Creator
- Approach Option #1 (Dispatch to Props) If we DELETE this optional
- Example: Chain Functions.
- Note:
- Cont’d
- Approach Option #2 (Dispatch to Props) - Use
mapDispatchToProps
instead of verbose way to dispatch an action (i.e.this.props.dispatch..
) by usingmapDispatchToProps
to wrap the Action in a call todispatch
so easy to use in function (i.e. onClickSave()), and triggers the flow through Redux.dispatch
is no longer injected by Connect as a Prop on our Component, as we are usingmapDispatchToProps
now socreateSkill
is injected instead
- Approach Option #2 (Dispatch to Props) - Use
- Cont’d
- Approach Option #3 (Dispatch to Props) - Using Helper function is less verbose than having to wrap
Action Creator in a
dispatch
call usingbindActionCreators
, which goes through allskillActions
and then wraps them in a call todispatch
(now we map to all Actions and expect Actions atthis.props.actions
to help separate Actions from Skills)
- Approach Option #3 (Dispatch to Props) - Using Helper function is less verbose than having to wrap
Action Creator in a
- Create Mock API to simulate delay calling API
(copy from https://github.com/coryhouse/pluralsight-redux-starter/tree/master/src/api)
- Create /src/api/mockSkillApi.js
- Create /src/api/delay.js (manages simulated delay of each Mock API call)
- Add Thunk Middleware to handle making Async calls
- Import and apply Redux Thunk Library as Middleware in store/configureStore.js
- Create Thunk to load skills when app loads initially (at bottom of src/actions/skillActions.js)
- Note: Action Creator Naming Conventions - postfix ‘Success’ as only called upon successful promise
- Create Success/Error Action Types called after API response (i.e.
LOAD_SKILLS_SUCCESS
)
- Cont’d
- Create handler in Reducers file corresponding to Action Thunk to update Redux State with response from Mock API
- Configure app entrypoint
index.js
so Fetches Skill data from Mock API when app initially loads. AfterconfigureStore()
we may start Dispatching Actions against the Store (alternative in server-rendering is to inject JSON data into header of index.html)
- Restructure app to achieve Separation of Concerns
- Create SkillsPage, SkillList, SkillListRow
- Create common/SelectInput and common/TextInput
- Container vs Child Components
- Container to list data (existing skills shown on page load after retrieval from Mock API). Skills loaded by displaying Action with Redux using Redux Thunks
- Child to manage add/edit data
- Move to Child the JSX markup, etc currently contained in SkillsPage
- Separate Pages for Adding Skills and Editing Skills (ManageSkills Component)
- Create Container Component using IntelliJ’s Final Template Feature to create
Redux Container Component with boilerplate already setup
- New > React Class Component
- New > React Redux Container Component
- New > React Stateless Component
- Populate form using
mapStateToProps
andcomponentWillReceiveProps
- Centralise declarations of initial State by creating reducers/initialState.js
- Reducers handle slices of the Store but provides centralised picture of how initialise object wrapped in Store
- Manage and display Status/Errors of Async Calls using Redux using dedicated Actions and Reducers
- Loading indicator when app first loads during API call to retrieve data
- Track and Display on progress of async call status when save form data (avoid redirecting before save complete and updates not immediately shown)
- Solution Use Loading Dots library as Preloader to display when AJAX call in progress. Add Toastr to show notifications
- Handle errors of rejected Promises elegantly (instead of allowing API calls to silently fail)
- Optimistic Updates are when want to immediately update UI with changes even before AJAX call has completed without showing Preloader (i.e. deleting a user) AJAX finishes behind the scenes.
Redirect URL using React Router Context Types
-
Context Types are global variables that Library authors use (avoids boilerplate plumbing code) but that other app developer consumers should avoid
-
Declare Context Types (Static Property) to import into Component
- Declare after Class Definition (since Context Types is Static Property)
Populate Form on page load (using Lifecycle Methods)
mapStateToProps
- initially populate form when mountedcomponentWillReceiveProps
- update local State when Props change
Lifecycle Method componentWillReceiveProps updates State when Props change
- Situation is initialising Container Component State based on Props.
After Container Component page initialises any changes to the Props
are not reflected in the Container Component State.
Lifecycle method
componentWillReceiveProps
used to update Container Component State when Props change, as it is called anytime the Props are changed. - Warning:
componentWillReceiveProps
may run even when Props have not changed
Container Component Structure Summary (using Redux)
- File Structure of Container Component
- Constructor
- Initialise State
- Call
bind
on functions to be bound tothis
context
- Child Functions (called by
render
function) render
function- Calls Child Component with JSX markup (Containers shouldn’t renders JSX inline)
propType
validations- Redux Connect and related functions (
mapStateToProps
andmapDispatchToProps
)
- Constructor
Destructuring
Automated Testing Stack using React and Redux
- Testing Stack:
- Testing frameworks
- Mocha - highly configurable
- Add Chai or Expect assertion library
- i.e. Chai (to.equal, etc, with No Spy built-in)
- i.e. Expect (toBe, etc, with Spy built-in)
- Add Chai or Expect assertion library
- Jasmine - less configurable
- Jest - wrapper over Jasmine (from Facebook) with unique features
- Tape - minimalistic
- AVA - robust like Mocha, but NEW unproven.
- Concurrent (not serial)
- Built-in assertions
- No globals
- Built-in ES6
- Runs only impacted tests on save
- Specific errors with marker
- Mocha - highly configurable
- Assertion libraries
- Helper libraries
- React Test Utils API (built by Facebook, verbose API)
shallowRender
- render in Isolation a single component (without Child-level Components), no DOM requiredrenderIntoDocument
- render Set of components with their Children, actually renders component into DOM using JSDom to simulate browser interaction. Supports use of refs and interactions (not supported yet inshallowRender
findRenderedDOMComponentWithTag
- query specific DOM element by TagscryRenderedDOMComponentWithTag
- find DOM elements by Tag- Simulate event interactions on component and assert based on outcomes
- Enzyme (PREFERRED)
- Abstraction that calls React Test Utils API behind the scenes to provide nicer API
- JSDOM used to provide in-memory DOM in Node.js
- Cheerio used for fast elegant jQuery-like selectors
- DOM interaction similar to jQuery https://github.com/airbnb/enzyme/blob/master/docs/api/shallow.md
shallow
- renders one level deepmount
- renders set of components (including Child Components) with full DOM loaded in memory using JSDOM
- React Test Utils API (built by Facebook, verbose API)
- Testing frameworks
- Testing Approaches
- Browser (i.e. Karma test runner)
- Headless Browser (i.e. PhantomJS)
- In-memory DOM (i.e. JSDOM)
- Simulates actual browser in-memory to interact with via Node.js
- Test Dumb Presentation Components
- Provide Props and expect Markup
- Automated Unit and Integration Tests using Redux
- Test Scope
- Test Components connected to Redux
- Test
mapStateToProps
by extracting out data selection or manipulation into separate pure function in src/selectors that are easy to test
- Test
- Test Redux Action Creators
- Simple as just checking return
- Test Thunks
- Requires Mock Store (using redux-mock-store) and HTTP Calls (using nock) with fake responses for testing
- Test Reducers
- Automate the creation of Reducer tests using “Redux Test Recorder” library https://github.com/conorhastings/redux-test-recorder
- Test Stores (and integration)
- Integration Test to ensure Actions, Store, Reducers are interacting as expected
- Test Components connected to Redux
- Test Sections
- Test Markup
- Test Behaviour (i.e. Components connected to Redux)
- Test Scope
Setup Production Build
- Goals
- Lint and run tests
- Bundle and minify JS and CSS
- Generate JS and CSS SourceMaps
- Exclude dev concerns
- Store
- src/store/configureStore does not require
reduxImmutableStateInvariant()
in prod
- src/store/configureStore does not require
- Webpack
- Define global env variable
process.env.NODE_ENV
- Use
devtool: 'source-map'
(slower than other) - Remove Webpack Hot Middleware in Prod
- Serving content from /dist folder instead of /src in Prod
- Use different Plugins
- Change CSS Loader
- Create tools/build.js file to run Webpack Prod build
- Create tools/buildHtml.js to copy src/index.html to dist/index.html
- Serve Prod build on local machine with tools/distServer.js (including GZIP compression library)
- Update package.json with Prod scripts
- Define global env variable
- Store
- Build React in prod mode
- Open prod build in browser
-
Reduce size of bundle.js (4.9 MB is far too large) to 120 kB
-
Run prod build locally with
npm run build
and go to localhost:3000 - Further optimisations:
- Remove babel-polyfill, toastr, jQuery, Bootstrap, and use custom React Router to reduce to ~50 kB
Debugging
- Insert
debugger;
in code and then run in browser to trigger breakpoints (usingdevtool: inline-source-map
of Webpack)- Hover over variables to display info
- Test values in console window
IDE
- Atom - Add packages react, and terminal-plus
- IntelliJ - Configur Languages & Frameworks > JavaScript > Change to ES6 or JSX Harmony/React JSX (for React)
IntelliJ’s File and Code Template Feature
- Preferences > Editor > File and Code Templates > Files
React Class Component
React Stateless Component
React Redux Container Component
Stack
- Redux, ES6 and Babel, React Router, Webpack, NPM, ESLint, Mocha, React Test Utils, Enzyme
- Note:
- NPM scripts used directly instead of using Gulp plugin abstraction and less points of potential failure http://bit.ly/npmvsgulp
JS Mutable vs Immutable
- Immutable (when change value of a type a new copy is created). High Performance
- Number, String, Boolean, Undefined
- Cont’d
- Change State Approaches (Non-Manually create copies of existing object)
- Object.assign(target, …sources)
- Allows specify existing object as template
- Object.assign(target, …sources)
- Change State Approaches (Non-Manually create copies of existing object)
- Mutable
- Objects, Arrays, Functions
Object.assign
(ES6 but requires babel-polyfill to transpile) is used in Reducers to return deep copy of existing State with requested changes to update State- Example Mutate State:
Handling Immutable State
- ES6 Reducers with:
Object.assign
- Spread operator
- ES5
- Lodash merge, extend
- Object-assign (NPM)
- Libraries
- react-addons-update
- Immutable.js
Enforce Immutability (not mutate Redux state)
- Trust team
- Safety net (redux-immutable-state-invariant) - only in Dev Mode
- Programmatic enforcement (Facebook Immutable.js) - creates immutable JS data structures
Immutability Benefits
- Clarity
- Centralised immutable Store ease of identifying where and how State change occurred (i.e in a Reducer). So more predictable and easier to reason about.
- Performance
- Mutable State would require expensive operation checking every single property to see if State actually changed (for large State object with many properties)
- Immutable State only requires a Reference Comparison (checking if old State not
referencing same object in memory to know if State changed)
i.e.
if (prevStoreState !== storeState)
. React-Redux library uses Reference Comparison to determine when to notify React Component of State changes. If NO changes thenshouldComponentUpdate
method is called.
- Time-Travel Debugging Experience
- Redux DevTools Extension allow travel through time whilst debugging and see each specific State change occur (and even undo/redo State changes to see how affect final state). Turn off specific Actions to see final state without certain Actions occurring. Playback at desired speed.
Reducers Handling of Data changes
- Store changes by Dispatch an Action handled by Reducer
- Pure Functions
- Return same value when called with same set of arguments
- Note: Return value depends only on value of parameters with no side-effects
- Forbidden in Reducers
- Mutation of arguments
- Perform API calls and routing transitions
- Call non-Pure Functions (i.e. date.now, Math.random)
- Single Store
- Single Reducer slices State changes into multiple smaller Reducers (for separate Data Domains)
- Avoids multiple Stores where have to wait for others
- All Reducers are called on each Dispatch from Single Store. Switch statements inside each Reducer function checks if the passed slice of State should be handled by that specific Reducer (so important that Reducers return untouched State as default if no switch case matches, as those that do not match simply return the state passed to them)
- Multiple Reducers allows handling different pieces of the Store in isolation
- “Reducer Composition” is where each Action may be handled by Multiple Reducers responsible for updating specific slices of State
Data Management Tools
- React setState, Flux/Redux, Relay
Hot Reloading
babel-preset-react-hmre
wraps Components in custom Proxy (with hooks to inject new implementations such as immediate apply changes without reload upon save) using Babel- Limitations:
- Only reloads functional Components where a class is up the hierarchy tree
- Does not reload all container functions (i.e.
mapStateToProps
)
Redux
- Manages app data flows
- Single Centralised Object Graph - avoids interactions handling multiple Stores
- Redux uses less boilerplate code than Flux and more power
- Components (top-level) subscribe to Redux Store
- Server-rendering of Components is possible (Isomorphic JS)
- Immutable Store offers Hot Reloading (live changes retains current client-side State), performance, Time Travel Debugging (step fwd/back and replay State)
- API is 2kB minified
Redux 3x Principles
- All app State in single immutable Store (State cannot be changed)
- Note: In contrast to Flux’s multi-Store model, single Store helps debugging, server rendering, and undo/redo
- Actions (describing user intent) emitted are the only way to trigger State mutations
- State mutations/updates performed by pure functions (Reducers)
- Reducers are pure functions that accept current State and Action and return new State
React + Redux Use Cases
- Basic app use Vanilla JS
- Mid-complex app use jQuery for changing DOM, AJAX calls
- Complex app use React and Redux for complex data flows, same data multiple places,
many state changes (ideally managed in single location for consistency and testability),
- Redux not useful for just displaying static data
- Redux useful for handling interactions between two Components without Parent/Child relationship or when they both use the same data by allowing them to communicate to ensure the data stays in synch by connecting them both to a Redux centralised Store (like local client-side database) that and each Component may dispatch an Action that updates the single Store. All Components connected to Store are immediately notified of data changes
- Redux useful when two Components manipulating same data (when non-hierarchical data)
- Redux useful for many Actions by offering structure and scalability
- High Setup time for React and Redux to be scalable, testable, maintainable, with rapid feedback for complex apps
Redux vs Flux
- Common
- Unidirectional data flow - Data flows down, Actions flow up
- Finite set of Actions utilised that define how State may be mutated
- Action Creators may be defined to generate Actions
- Action Types (constants may be used to prevent typos)
- Store constants in src/actions/actionTypes.js
- Store that holds State
- Redux Only
- Single Store only allowed (single source of truth avoids storing same data in multiple places, and complexity of handling interactions between Stores)
- Reducers (pure functions) accept current State and Action and return new State
- Reducers
- Container Components (React Components) with logic for marshalling Data and Actions passed down to separate Dumb Components that use pure functions to receive the data via Props (ease of testing and reuse)
- Redux Store is immutable and need approaches to deal with immutable data in Reducers
- Redux does NOT have a Dispatcher, instead easily composes Reducers (pure functions)
- Actions are each handled by one or more Reducers that update Single Store
- Reducer updates Store with new updated copy of State (since State is immutable)
- Reducers specify how State changes for given Action
- Redux SEPARATES State and Logic of changing state using Single Responsibility Principle:
- Reducers handle Logic for returning new updated copy of state (since State is immutable in Redux Store)
- Single Store contains only State
- Single Store passes Actions down to defined Root Reducers
- Multiple Reducers (optionally Nested via Functional Composition like how React Components may be Nested) to handle complex Stores with multiple potential Actions
- React-Redux library used that contains a Connect method that generates a top-level React Component connected to Actions and Single Redux Store automatically. All changes to Store State causes a function to be called triggering a React Component re-render
-
Dispatching Actions affects the data in the Store
- Example Action Creator:
- Cont’d
- Example Action:
- Cont’d
- Example Store:
- Cont’d
- Redux Store API
- Cont’d
- Example Reducer:
-* Action - describes user intent contains type/data
| | where data portion may be multiple objects and metadata
| | (i.e. action for rating skill { type: SKILL_RATE, rating: 2 } )
| |
| *
| Store —– Reducers
| | —–
| *
-
React
-
Flux Only
- Multiple Stores allowed
- Concepts are Actions, Dispatchers, Stores
- Actions triggered causes Singleton Dispatcher to notify Stores
- Stores explicitly connect to Dispatcher using EventEmitter to know about Actions
- Stores contain BOTH State and also Logic to change the state
- Different Stores are disconnected but interact via WaitFor function
- Stores are flat (NOT Nested)
- Flux Store is mutable
- Explicitly Subscribe the React Views to Stores using OnChange handlers and EventEmitter
React-Redux Library
- react-redux Library connects React Components to Redux
- react-redux Library is companion library for Redux
- react-redux Library connects React Router Container Components to Redux
- react-redux Library used between Store/Reducers and React Components
- React-Redux generates Container Components
(React Components that use
store.subscribe
to read part of Redux state tree and supply Props to Child Components) - React-Redux library comprises:
- Provider Component - Attaches React App to Store
- Wraps entire app at root top-level Component, only used once when root renders. Makes Redux Store available to all Container Components automatically (using React context) Attaches app to Redux Store (otherwise would have to explicitly pass Store to all Components requiring it individually)
- Note: React context is a feature for Library authors (not developers)
- Provider Component - Attaches React App to Store
- Cont’d
- Connect function - Creates Container Component
- Wraps Component so connected to Redux Store
(alternatively in Flux we manually add
addChangeListener/removeChangeListener(this.onChange
andonChange() { this.setState({ skills: SkillStore.getAll...
to each Store insidecomponentWillMount
) - Declare part of Redux Store to attach to Component as Props
- Declare Actions to expose on Props
- Creates container Components automatically
- Redux
connect
Benefits:- Not need manual subscribe/unsubscribe to Store, as
connect
does it automatically - Not need Lifecycle Methods to subscribe/unsubscribe to Store in Redux,
as Redux allows all to be Stateless “functional” Components (avoiding
this
keyword confusion), whereas Flux requires use ofcomponentWillMount
andcomponentWillUnmount
so cannot use React Stateless “functional” Components for Container Components when working in Flux (since Stateless ones do not have Lifecycle Methods) - Redux allows explicitly exposing subsets of State to Container Component (performance improvements
as only renders when specific data that is connected changes, saving unnecessary renders or manual
suppression of them using
shouldComponentUpdate
), whereas in Flux after wireonChange
listener to Store then entire Store data is exposed
- Not need manual subscribe/unsubscribe to Store, as
mapStateToProps
function passed toconnect
function specifies State to expose to Component as Props, causing Component to subscribe to Redux Store and gets called when Store is updated. This function returns and object whose properties become available as Props of Container Component- Filter/transform/sort State here ready for use in Container Component
- Prepare data (transform) to prepopulate forms
mapDispatchToProps
function is passed toconnect
function the Actions to expose to Component.mapStateToProps
function is called every time Component is called- Use Reselect Library to increase performance if any expensive operations (i.e.
change list, calculations, etc)
being performed in
mapStateToProps
. It memoizes (caching for function calls) to keep track of results of each function call to increase performance, so if function called again with same parameters it just returns memoized value and but doesn’t need call function again
- Use Reselect Library to increase performance if any expensive operations (i.e.
change list, calculations, etc)
being performed in
mapDispatchToProps
function allows specify what Actions to expose at Props. See section “3x Ways to Handle passing Actions to Props in Redux Container Components”
- Wraps Component so connected to Redux Store
(alternatively in Flux we manually add
- Connect function - Creates Container Component
- Cont’d
- Cont’d
- Example 1: Simple app w 1x Container Component and 1x Reducer
- Pass down all State to Presenter Component via Props
- Expose only part of Store’s State by specifying within return block
- Example 1: Simple app w 1x Container Component and 1x Reducer
- Cont’d
- Cont’d
- Cont’d
- Example 2: Complex
- Create Multiple Container Components (to manage sections of app)
- Create different Reducers to handle diff slices of Stores
- Example 2: Complex
- Cont’d
3x Ways to Handle passing Actions to Props in Redux Container Components (i.e. for use with mapDispatchToProps
)
- 1) Use Dispatch Directly (not use
mapDispatchToProps
).dispatch
Prop may be manually called and passed an ActionCreatorthis.props.dispatch(loadSkills());
so thedispatch
Prop calls the ActionCreators.
Ignore adding mapDispatchToProps
(Optional Param) as param of connect
that automatically causes dispatch
function to attach itself to React Container Component.
Disadvantages: Extra boilerplate explicitly calling dispatch
each time that has been passed
an Action to fire off; Child Components become tied to Redux concepts (i.e. dispatch function
and ActionCreators)
- 2) Manually wrap within
mapDispatchToProps
our calls toActionCreator
anddispatch
(results in more code withinmapDispatchToProps
than in the React Container Component). Explicitly specify Actions to expose to React Container Component (each ActionCreator wrapped in adispatch
call)
Example:
- 3) Use
bindActionCreators
convenience function that wraps ActionCreators passed to it in dispatch calls for us (saving us having to do it manually like in Option 2) so easy to pass down to Child Componets (does Option 2 approach to expose Actions to React Container Components, but automatically)
Setup Environment
- Dependencies
babel-polyfill
- Covers ES6 features that do not simply transpile from ES6 to ES5 (i.e. array.from, set, map, promise, generators, etc) listed as “Additional polyfill needed” at Babel Learn ES2015 Note:babel-polyfill
is large (50kB) so consider injecting only subsets of polyfill dependencies
React Component Approaches
- React Component Creation Approaches
- ES5
.createClass
(original React approach)- Autobinded functions to Components’
this
context - PropTypes declared inside class definition
- Initialise State in
setInitialState
method
- Autobinded functions to Components’
- ES5 stateless function style (simpler syntax where React assumes that
return
is therender
function (i.e.var Hi = function(props) { return (<h1></h1>); });
- Use when receive data only from Props (immutable), and where Component NOT manage State, utilise Component lifecycle methods, or performance optimisations
- ES6
.class
- No autobinding of functions to Components’
this
context that changes depending on the caller.- Solution options (setup ESLint rules to ensure comply):
- Inline
.bind
in Render function - Explicit bind is required (i.e.<div onClick={this.handleClick.bind(this)}></div>
- Constructor binding (preferred for performance) - (i.e.
class ___ extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this) } ...
)
- Inline
- Solution options (setup ESLint rules to ensure comply):
- PropTypes and Default Props declared BELOW class definition (unless enable experimental Stage 1 JS proposal in Babel for class fields and static properties inside class)
- Initialise State in Component
constructor
method
- No autobinding of functions to Components’
- ES6 stateless function style
(i.e.
const Hi = (props) => { return (<h1></h1>); });
(const
prevents accidental reassignment of Component) - Others including:
Object.create
, Mixins, Parasitic Components, StampIt
- ES5
- Non-React Class Creation Approaches
- Reflect APIs
Reflect.construct
- Reflect APIs
- Container vs Presentational Components for reusability, maintainability, and scalability
Class Component Style vs Stateless Component (Functional) Style
- Benefits of Stateless Functional Components (pure functions that receive Props and output HTML)
- No Classes required (simplifieds to plain functions, removes
extends
andconstructor
, avoid thethis
keyword and having to usebind(this)
to pass context around) - No support for State or lifecycle methods (protects/prevents developer from lazily hacking in local State)
- Less code (noise/bloat, code smells) required. Simple assertions with isolated tests (less Mocking, state changes, etc)
- Destructure Props in ES6 so all data specified in single function argument. Clear Component dependencies
- Usage: Dumb Presentational Components (focussed on UI rather than State/behaviour)
- Note: State management belong in high-level Container Components and Flux/Redux
- No Classes required (simplifieds to plain functions, removes
- Example Class Style
- Usage: Require State, require Refs to underlying DOM, require lifecycle methods, require Nested Components (Child functions where each render creates new instance)
- Use class for top-level Components to prevent issue with Hot Reloading
- Example Stateless Functional Component Style (equivalent)
- Usage: No State, No Refs (as does not create Component instance so Ref is null), No lifecycle methods, No nesting (as performance problem)
Organising Components
- Separate folders for Container/Presentation or by Feature
Container Component vs Presentation Component (two types of React Components)
- Container (Parent/Smart/Stateful/Controller View) (back-end of the front-end)
- Container Components are easily generated by react-redux Library
- Container Components use React Store.subscribe to read part of Redux State tree and supply Props to Child Components (when generated with react-redux Library additional performance optimisations are included)
- Behaviour
- Marshalling Data and passing to Child Components
- Actions passed to Child Components
- Stateful (manage State)
- Created using Redux connect
- Subscribe to Redux State
- Dispatch Redux Actions to Store (and connecting to it with connect)
- Aware of Redux
- No Markup
- Note: May have multiple Container Component levels
- Presentational (Child/Dumb/Stateless/View)
- Dumb (no logic)
- Receive and read Data and Actions functions via Props to display UI using render function that defines markup
- Fire Actions by invoking callbacks passed down to them via Props (so not tied to specific Behaviour)
- Stateless functional components (written by hand)
- No dependency/awareness of Redux, Actions, or Stores (easier to reuse)
- Not specify how Data loaded or mutated
- Markup
Async in Redux
-
i.e. AJAX calls to server to load data via an API on page load
- Mock API Pattern for client-side apps
- Rule of coding to Interface over an Implementation
- Benefits
- Not reliant on other devs or finalised API to build UI
- RAD with fast responses (may mask performance issues but we test against actual API before deployment)
- Test how UI performs when calls to API are slow/fast
by using
setTimeout
in Mock API to delay responses
- Flux vs Redux
- Flux handles Async API calls in Action
- Redux handles Async API calls using Redux Async Middleware Library such as:
Note: Redux Actions are Synchronous and return an object
- redux-thunk
- Return Functions (instead of Objects) from Action Creators
- Thunk wraps async operation in a function
- a function that returns a function
- a function that wraps an expression to delay its evaluation
- a thunk always returns a function that accepts dispatch as parameter
- Difficult Testing as must use Mock API without easy hooks to observe individual steps in async flow
- Easy to learn
- redux-thunk
- Cont’d
- Cont’d
- redux-saga
- ES6 Generators used along with an async DSL
- Generators handle async operations
- Generators are functions that may be paused/resumed later
and contain multiple
yield
statements (where generator pauses) - Testing easy as can
assert
on effects that return data (no Mocking required) - Hard to learn and large API and need to understand possible errors
- redux-promise (less popular)
- Flux standard Actions/promises used to provide clear conventions to async calls
- redux-saga
- Cont’d
-
Async Libraries for async flows using Redux
- Thunk to implement Redux flows