SlideShare ist ein Scribd-Unternehmen logo
1 von 80
Downloaden Sie, um offline zu lesen
Migrating from Flux to Redux
Boris Nadion
boris@astrails.com
@borisnadion
why and how
astrails
awesome web apps since 2005
flux? redux?
Apr-2015
backbone and marionette
React - breath of fresh air
best thing since jQuery
spaghetti -> MVC
V = React
M+C = ?
obviously Flux
npm install flux --save
hold on!
is there anything better?
hell yeah!
lummox
Alt
Flexor
Flux This
MartyJS
McFly
Flexible
Delores
Lux
Reflux
OmniscientJS
Fluffy
Material Flux
…
each one of them was better
…then any other
reflux is better than flux because […]
Flummox is better than flux because […]
alt is better than flux because […]
Flux from FB
npm install flux --save
Action Dispatcher Store
Action
View
API
view action
server action
useless dashboard
code review
https://github.com/astrails/react-
presentation-flux-redux
tag: #flux
1 export default class HackerNews extends Component {
2 constructor() {
3 super(...arguments);
4 this.state = HackerNewsStore.getNews();
5 this.onNewsUpdated = this.onNewsUpdated.bind(this);
6 }
7
12 componentWillMount() {
13 HackerNewsStore.addChangeListener(this.onNewsUpdated);
14 }
15
16 componentWillUnmount() {
17 HackerNewsStore.removeChangeListener(this.onNewsUpdated);
18 }
19
20 onNewsUpdated() {
21 this.setState(HackerNewsStore.getNews());
22 }
1 export default class Locations extends Component
2 constructor() {
3 super(...arguments);
4 this.state = LocationsStore.getLocations();
5 this.onLocationsChanged = this.onLocationsCha
6 }
7
8 componentWillMount() {
9 LocationsStore.addChangeListener(this.onLocat
10 }
11
12 componentWillUnmount() {
13 LocationsStore.removeChangeListener(this.onLo
14 }
15
16 onLocationsChanged() {
17 this.setState(LocationsStore.getLocations());
18 }
duplication
sync state <- store
direct access to actions and store
image: http://www.tech-dojo.org/?_escaped_fragment_=/articles/564326afd862ff0b00ef3cc9
smart and dumb
1 render() {
2 const allYouNeedIsHere = ...; // functions and data
3 return (
4 <DumpComponent { ...allYouNeedIsHere } />
5 );
6 }
// const NewsEntry = (props, context) => {};
const NewsEntry = ({ id, title }) => {
if (title) {
return (
<div>
<a href={ `...${id}` } target="_blank">{ title }</a>
</div>
);
}
return (
<div>Loading entry data...</div>
);
};
pure function
maintain its own state
but don’t access actions and store
indeed reusable
less talk, more work
refactor actions
1 const FormActions = {
2 addLocation: (location) => {
3 handleViewAction({
4 type: Constants.Form.ADD_LOCATION,
5 location
6 });
7 RemoteApi.queryTheWeather(location);
8 }
9 };
1 export const addLocation = (location) => {
2 handleViewAction({
3 type: Constants.Form.ADD_LOCATION,
4 location
5 });
6 RemoteApi.queryTheWeather(location);
7 };
1 const LocationActions = {
2 refresh: (location) => {
3 handleViewAction({ type:
Constants.Location.REFRESHING, location });
4 RemoteApi.queryTheWeather(location);
5 },
6
7 querySuccess: (data, location) => {
8 handleServerAction({
9 type:
Constants.Location.QUERY_SUCCESS,
10 data,
11 location
12 });
13 },
14
15 queryFailed: (error) => {
16 handleServerAction({
17 type: Constants.Location.QUERY_FAILED,
18 error
19 });
20 },
21
22 remove: (locationID) => {
23 handleViewAction({ type:
Constants.Location.REMOVE, locationID });
24 }
25 };
1 export const refresh = (location) => {
2 handleViewAction({ type: Constants.Location.RE
3 RemoteApi.queryTheWeather(location);
4 };
5
6 export const remove = (locationID) => {
7 handleViewAction({ type: Constants.Location.RE
8 };
9
10 export const querySuccess = (data, location) =>
11 handleServerAction({
12 type: Constants.Location.QUERY_SUCCESS,
13 data,
14 location
15 });
16 };
17
18 export const queryFailed = (error) => {
19 handleServerAction({
20 type: Constants.Location.QUERY_FAILED,
21 error
22 });
23 };
refactor components
1 const Home = () => (
2 <div className={ classes.home }>
3 <h2>Useless dashboard, flux version</h2>
4 <Form />
5 <Locations />
6 <HackerNews />
7 </div>
8 );
1 const Home = () => (
2 <div className={ classes.home }>
3 <h2>Useless dashboard, flux version</h2>
4 <Weather />
5 <HackerNews />
6 </div>
7 );
1 import { addLocation } from 'fluxx/actions/form';
2 import { refresh, remove } from 'fluxx/actions/location';
3
4 const Weather = () => (
5 <div>
6 <Form addLocation={ addLocation } />
7 <Locations remove={ remove } refresh={ refresh } />
8 </div>
9 );
export default class Locations extends Component {
static propTypes = {
remove: PropTypes.func.isRequired,
refresh: PropTypes.func.isRequired
};
//…
render() {
return (
<div className={ classes.locations }>
{ this.state.locations.map(location => <Location { ...this.props } { ...location } key={ location.uniqId } />) }
</div>
);
}
}
import React from 'react';
import classes from './location.sass';
const Location = ({ remove, refresh, status, temperature, icon_url: iconUrl, text, location, uniqId }) => {
const removeLocation = (e) => {
e.preventDefault();
remove(uniqId);
};
const refreshLocation = (e) => { /* … */ };
const renderState = () => { /* … */ };
return (
<div className={ classes.location }>
<a className={ classes.remove } href="#" onClick={ removeLocation }>&times;</a>
<div className={ classes.name }>
{ location }
</div>
{ renderState() }
</div>
);
};
export default Location;
1 export default class HackerNews extends Component {
2 constructor() {
3 super(...arguments);
4 this.state = HackerNewsStore.getNews();
5 this.onNewsUpdated = this.onNewsUpdated.bind(this);
6 }
7
12 componentWillMount() {
13 HackerNewsStore.addChangeListener(this.onNewsUpdated);
14 }
15
16 componentWillUnmount() {
17 HackerNewsStore.removeChangeListener(this.onNewsUpdated);
18 }
19
20 onNewsUpdated() {
21 this.setState(HackerNewsStore.getNews());
22 }
1 export default class Locations extends Component
2 constructor() {
3 super(...arguments);
4 this.state = LocationsStore.getLocations();
5 this.onLocationsChanged = this.onLocationsCha
6 }
7
8 componentWillMount() {
9 LocationsStore.addChangeListener(this.onLocat
10 }
11
12 componentWillUnmount() {
13 LocationsStore.removeChangeListener(this.onLo
14 }
15
16 onLocationsChanged() {
17 this.setState(LocationsStore.getLocations());
18 }
this shit
options?
mix-ins
aren't they dead?
Mixins Are Dead. Long Live Composition
https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-
components-94a0d2f9e750
react-redux
aware of components and flow control
redux
single source of truth
state is read-only
changes are made with pure functions
x!
Action Dispatcher
Action
View
API
view action
server action
Store
Store View
Action
API
D
http://www.keepcalm-o-matic.co.uk/p/keep-calm-and-let-the-fun-begin-4/
npm i redux --save
npm i react-redux —save
npm i redux-thunk —save
npm i redux-logger --save
containers and components
define some terms
components
(presentational components), have no idea about redux
containers
aware of redux, have no own React code
container = smart
component = dumb (less smart)
refactoring plan
api -> better api ;-)
actions -> actions
stores -> reducers
components & containers
API
import { querySuccess, queryFailed } from 'fluxx/actions/location';
queryTheWeather: (location) => {
SuperAgent.get(`…${location}&APPID=${APIKEY}`).set(‘Accept’, 'application/json').
end((error, res) => {
if (isSuccess(res)) {
querySuccess(res.body, location);
} else {
queryFailed(res);
}
});
},
queryTheWeather: ({ {location}, onSuccess, onFailure }) => {
SuperAgent.get(`…${location}&APPID=${APIKEY}`).set(‘Accept’, 'application/json').
end((error, res) => {
if (isSuccess(res)) {
onSuccess(res.body);
} else {
onFailure(res);
}
});
},
not aware of actions
actions
const querySuccess = (data, location) => ({ type: Constants.Location.QUERY_SUCCESS, data, loca
const queryFailed = (error) => ({ type: Constants.Location.QUERY_FAILED, error });
const startAddingLocation = (location) => ({ type: Constants.Form.ADD_LOCATION, location });
export const addLocation = (location) => (dispatch) => {
dispatch(startAddingLocation(location));
RemoteApi.queryTheWeather({
payload: { location },
onSuccess: (data) => {
dispatch(querySuccess(data, location));
},
onFailure: (error) => {
dispatch(queryFailed(error));
}
});
};
for each async action, usually there are 3 sync actions:
starting the request (update the UI)
request succeed
request failed
reducers
const locations = createReducers(DEFAULT_STATE, {
[Constants.Form.ADD_LOCATION]: (state, action) => {
const { location } = action;
// FIXME: yeah, need a better uniq id
return [...state, { location, uniqId: location, status: LOOKING_OUTSIDE }];
},
[Constants.Location.QUERY_SUCCESS]: (state, action) => updateState(state, acti
[Constants.Location.REFRESHING]: (state, action) => updateState(state, action.
[Constants.Location.REMOVE]: (state, action) => removeLocation(state, action.l
// so what, do your own error handling ;-)
[Constants.Location.QUERY_FAILED]: (state) => state
});
switch (action.type) {
case Constants.Form.ADD_LOCATION:
// FIXME: yeah, need a better uniq id
locations.push({
location: action.location,
status: "Looking outside...",
uniqId: action.location
});
return LocationsStore.emitChange();
case Constants.Location.QUERY_SUCCESS:
updateLocation(action.location, action.data);
return saveChanges();
case Constants.Location.REMOVE:
removeLocation(action.locationID);
return saveChanges();
case Constants.Location.REFRESHING:
updateState(action.location, "Refreshing data...");
return saveChanges();
default:
return true;
}
switch (action.type) {
case Constants.HackerNews.TOP_IDS_LOADED:
newsIds = action.ids;
return HackerNewsStore.emitChange();
case Constants.HackerNews.STORY_FETCHED:
news[action.id] = action.data;
return HackerNewsStore.emitChange();
default:
return true;
}
const hackerNews = createReducers(DEFAULT_STATE, {
[Constants.HackerNews.TOP_IDS_LOADED]: (state, action) => ({ ...state, newsIds: action.ids }),
[Constants.HackerNews.STORY_FETCHED]: (state, action) => ({ ...state, news: { ...state.news, [action.id]:
action.data } })
});
creating store
const allowDebugging = (process.env.NODE_ENV !== 'production') ||
(localStorage && localStorage.getItem('reactDebug') === 'yes');
export const buildStore = () => {
const rootReducer = combineReducers({ locations, hackerNews });
const devToolsExt = (allowDebugging && window.devToolsExtension) ?
window.devToolsExtension() :
f => f;
const middleWare = allowDebugging ?
applyMiddleware(thunkMiddleware, createLogger()) :
applyMiddleware(thunkMiddleware);
const store = createStore(
rootReducer,
undefined,
compose(middleWare, devToolsExt)
);
return store;
};
components & containers
import HackerNews from 'containers/hacker_news';
import Weather from 'containers/weather';
const store = buildStore();
const Home = () => (
<Provider store={ store }>
<div className={ classes.home }>
<h2>Useless dashboard, redux version</h2>
<Weather />
<HackerNews />
</div>
</Provider>
);
import { addLocation, refresh, remove } from 'reduxx/actions/locations';
import Weather from 'components/weather';
import { connect } from 'react-redux';
const mapDispatchToProps = (dispatch) => ({
addLocation: (location) => dispatch(addLocation(location)),
refresh: (location) => dispatch(refresh(location)),
remove: (location) => dispatch(remove(location))
});
const mapStateToProps = (state) => ({ locations: state.locations });
export default connect(mapStateToProps, mapDispatchToProps)(Weather);
container
component
import React, { PropTypes } from 'react';
import Form from 'components/form';
import Locations from 'components/locations';
const Weather = ({ addLocation, refresh, remove, locations }) => (
<div>
<Form addLocation={ addLocation } />
<Locations remove={ remove } refresh={ refresh } locations={ locations } />
</div>
);
https://github.com/astrails/react-
presentation-flux-redux
tag: #redux
▾ src/
▾ components/
▾ hacker_news/
hacker_news.sass
index.js
▾ home/
home.sass
index.js
▾ location/
index.js
location.sass
▾ locations/
index.js
locations.sass
form.js
news_entry.js
weather.js
▾ containers/
hacker_news.js
weather.js
▾ src/
▾ components/
form.js
hacker_news.js
hacker_news.sass
home.js
home.sass
location.js
location.sass
locations.js
locations.sass
news_entry.js
move files to folders - less mess
adding propTypes
kinda documentation you’ll use
"react/prop-types": 2,
const Location = ({ remove, refresh, status, temperature, icon_url: iconUrl, text, location, uniqId }) => {
//…
};
Location.propTypes = {
remove: PropTypes.func.isRequired,
refresh: PropTypes.func.isRequired,
status: PropTypes.string.isRequired,
temperature: PropTypes.number,
icon_url: PropTypes.string,
text: PropTypes.string,
location: PropTypes.string.isRequired,
uniqId: PropTypes.string.isRequired
};
export default class HackerNews extends Component {
static propTypes = {
hackerNews: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
title: PropTypes.string
})),
fetch: PropTypes.func.isRequired
};
//…
}
https://flic.kr/p/7EsetZ
newsItem: PropTypes.object
newsItem: PropTypes.shape({…})
https://github.com/astrails/react-
presentation-flux-redux
branch: master
http://io9.gizmodo.com/5794957/see-darth-vader-riding-a-unicorn-it-is-your-destiny/
thanks!
Q&A
Boris Nadion
http://astrails.com

Weitere ähnliche Inhalte

Was ist angesagt?

Solid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon ItalySolid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon ItalyFabio Collini
 
Kotlin : Advanced Tricks - Ubiratan Soares
Kotlin : Advanced Tricks - Ubiratan SoaresKotlin : Advanced Tricks - Ubiratan Soares
Kotlin : Advanced Tricks - Ubiratan SoaresiMasters
 
RxJS - 封裝程式的藝術
RxJS - 封裝程式的藝術RxJS - 封裝程式的藝術
RxJS - 封裝程式的藝術名辰 洪
 
Universal JavaScript
Universal JavaScriptUniversal JavaScript
Universal JavaScript名辰 洪
 
Introducing Vuex in your project
Introducing Vuex in your projectIntroducing Vuex in your project
Introducing Vuex in your projectDenny Biasiolli
 
A clean(er) architecture
A clean(er) architectureA clean(er) architecture
A clean(er) architectureAndreaMaglie
 
Source Code for Dpilot
Source Code for Dpilot Source Code for Dpilot
Source Code for Dpilot Nidhi Chauhan
 
Dpilot Source Code With ScreenShots
Dpilot Source Code With ScreenShots Dpilot Source Code With ScreenShots
Dpilot Source Code With ScreenShots DeepAnshu Sharma
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsSam Hennessy
 
exportDisabledUsersRemoveMailbox
exportDisabledUsersRemoveMailboxexportDisabledUsersRemoveMailbox
exportDisabledUsersRemoveMailboxDaniel Gilhousen
 
Quick start with React | DreamLab Academy #2
Quick start with React | DreamLab Academy #2Quick start with React | DreamLab Academy #2
Quick start with React | DreamLab Academy #2DreamLab
 

Was ist angesagt? (19)

Rxjs swetugg
Rxjs swetuggRxjs swetugg
Rxjs swetugg
 
Solid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon ItalySolid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon Italy
 
Angular mix chrisnoring
Angular mix chrisnoringAngular mix chrisnoring
Angular mix chrisnoring
 
Rxjs ngvikings
Rxjs ngvikingsRxjs ngvikings
Rxjs ngvikings
 
Nativescript angular
Nativescript angularNativescript angular
Nativescript angular
 
Easy Button
Easy ButtonEasy Button
Easy Button
 
Kotlin : Advanced Tricks - Ubiratan Soares
Kotlin : Advanced Tricks - Ubiratan SoaresKotlin : Advanced Tricks - Ubiratan Soares
Kotlin : Advanced Tricks - Ubiratan Soares
 
RxJS - 封裝程式的藝術
RxJS - 封裝程式的藝術RxJS - 封裝程式的藝術
RxJS - 封裝程式的藝術
 
Universal JavaScript
Universal JavaScriptUniversal JavaScript
Universal JavaScript
 
Introducing Vuex in your project
Introducing Vuex in your projectIntroducing Vuex in your project
Introducing Vuex in your project
 
A clean(er) architecture
A clean(er) architectureA clean(er) architecture
A clean(er) architecture
 
Source Code for Dpilot
Source Code for Dpilot Source Code for Dpilot
Source Code for Dpilot
 
Dpilot Source Code With ScreenShots
Dpilot Source Code With ScreenShots Dpilot Source Code With ScreenShots
Dpilot Source Code With ScreenShots
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 
exportDisabledUsersRemoveMailbox
exportDisabledUsersRemoveMailboxexportDisabledUsersRemoveMailbox
exportDisabledUsersRemoveMailbox
 
Firebase ng2 zurich
Firebase ng2 zurichFirebase ng2 zurich
Firebase ng2 zurich
 
Lucene
LuceneLucene
Lucene
 
Quick start with React | DreamLab Academy #2
Quick start with React | DreamLab Academy #2Quick start with React | DreamLab Academy #2
Quick start with React | DreamLab Academy #2
 
Side effects-con-redux
Side effects-con-reduxSide effects-con-redux
Side effects-con-redux
 

Andere mochten auch

Andere mochten auch (20)

Thinking Reactively
Thinking ReactivelyThinking Reactively
Thinking Reactively
 
Designing applications with Redux
Designing applications with ReduxDesigning applications with Redux
Designing applications with Redux
 
Flux and redux
Flux and reduxFlux and redux
Flux and redux
 
Reducers+flux=redux
Reducers+flux=reduxReducers+flux=redux
Reducers+flux=redux
 
Redux vs Alt
Redux vs AltRedux vs Alt
Redux vs Alt
 
React. Flux. Redux
React. Flux. ReduxReact. Flux. Redux
React. Flux. Redux
 
Windows 7
Windows 7Windows 7
Windows 7
 
Health report
Health reportHealth report
Health report
 
Capitulo 5 de el libro blanco de las tic
Capitulo 5 de el libro blanco de las ticCapitulo 5 de el libro blanco de las tic
Capitulo 5 de el libro blanco de las tic
 
Frd nafs
Frd nafsFrd nafs
Frd nafs
 
Transformaciones de procesos
Transformaciones de procesosTransformaciones de procesos
Transformaciones de procesos
 
The lost ones
The lost onesThe lost ones
The lost ones
 
Intro matlab msantos
Intro matlab msantosIntro matlab msantos
Intro matlab msantos
 
Edgar allan poe
Edgar allan poeEdgar allan poe
Edgar allan poe
 
Estructuras organizacionales
Estructuras organizacionalesEstructuras organizacionales
Estructuras organizacionales
 
Product-Market Growth Charts
Product-Market Growth ChartsProduct-Market Growth Charts
Product-Market Growth Charts
 
004. Working with React component
004. Working with React component004. Working with React component
004. Working with React component
 
3 ficha refuerzo 1º eso
3 ficha  refuerzo    1º   eso3 ficha  refuerzo    1º   eso
3 ficha refuerzo 1º eso
 
17 ficha refuerzo 1º eso
17 ficha   refuerzo  1º  eso17 ficha   refuerzo  1º  eso
17 ficha refuerzo 1º eso
 
G.2014-immuno~ (18.immunologic application-xm)
 G.2014-immuno~ (18.immunologic application-xm) G.2014-immuno~ (18.immunologic application-xm)
G.2014-immuno~ (18.immunologic application-xm)
 

Ähnlich wie Migrating from Flux to Redux. Why and how.

React, Redux and es6/7
React, Redux and es6/7React, Redux and es6/7
React, Redux and es6/7Dongho Cho
 
You will learn RxJS in 2017
You will learn RxJS in 2017You will learn RxJS in 2017
You will learn RxJS in 2017名辰 洪
 
Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Robert DeLuca
 
JS Fest 2019. Glenn Reyes. With great power comes great React hooks!
JS Fest 2019. Glenn Reyes. With great power comes great React hooks!JS Fest 2019. Glenn Reyes. With great power comes great React hooks!
JS Fest 2019. Glenn Reyes. With great power comes great React hooks!JSFestUA
 
Higher Order Components and Render Props
Higher Order Components and Render PropsHigher Order Components and Render Props
Higher Order Components and Render PropsNitish Phanse
 
Reactive programming with RxJS - ByteConf 2018
Reactive programming with RxJS - ByteConf 2018Reactive programming with RxJS - ByteConf 2018
Reactive programming with RxJS - ByteConf 2018Tracy Lee
 
React Native: Developing an app similar to Uber in JavaScript
React Native: Developing an app similar to Uber in JavaScriptReact Native: Developing an app similar to Uber in JavaScript
React Native: Developing an app similar to Uber in JavaScriptCaio Ariede
 
Building Smart Async Functions For Mobile
Building Smart Async Functions For MobileBuilding Smart Async Functions For Mobile
Building Smart Async Functions For MobileGlan Thomas
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPresswpnepal
 
Going fullstack React(ive) - Paulo Lopes - Codemotion Amsterdam 2017
Going fullstack React(ive) - Paulo Lopes - Codemotion Amsterdam 2017Going fullstack React(ive) - Paulo Lopes - Codemotion Amsterdam 2017
Going fullstack React(ive) - Paulo Lopes - Codemotion Amsterdam 2017Codemotion
 
Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Morris Singer
 
Object-Oriented JavaScript
Object-Oriented JavaScriptObject-Oriented JavaScript
Object-Oriented JavaScriptkvangork
 
Object-Oriented Javascript
Object-Oriented JavascriptObject-Oriented Javascript
Object-Oriented Javascriptkvangork
 
Building complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and ReactBuilding complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and ReactJonne Kats
 
Creating an Uber Clone - Part XV.pdf
Creating an Uber Clone - Part XV.pdfCreating an Uber Clone - Part XV.pdf
Creating an Uber Clone - Part XV.pdfShaiAlmog1
 
State manager in Vue.js, from zero to Vuex
State manager in Vue.js, from zero to VuexState manager in Vue.js, from zero to Vuex
State manager in Vue.js, from zero to VuexCommit University
 

Ähnlich wie Migrating from Flux to Redux. Why and how. (20)

React, Redux and es6/7
React, Redux and es6/7React, Redux and es6/7
React, Redux and es6/7
 
You will learn RxJS in 2017
You will learn RxJS in 2017You will learn RxJS in 2017
You will learn RxJS in 2017
 
Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React
 
JS Fest 2019. Glenn Reyes. With great power comes great React hooks!
JS Fest 2019. Glenn Reyes. With great power comes great React hooks!JS Fest 2019. Glenn Reyes. With great power comes great React hooks!
JS Fest 2019. Glenn Reyes. With great power comes great React hooks!
 
Higher Order Components and Render Props
Higher Order Components and Render PropsHigher Order Components and Render Props
Higher Order Components and Render Props
 
React redux
React reduxReact redux
React redux
 
Reactive programming with RxJS - ByteConf 2018
Reactive programming with RxJS - ByteConf 2018Reactive programming with RxJS - ByteConf 2018
Reactive programming with RxJS - ByteConf 2018
 
Why react matters
Why react mattersWhy react matters
Why react matters
 
React Native: Developing an app similar to Uber in JavaScript
React Native: Developing an app similar to Uber in JavaScriptReact Native: Developing an app similar to Uber in JavaScript
React Native: Developing an app similar to Uber in JavaScript
 
Building Smart Async Functions For Mobile
Building Smart Async Functions For MobileBuilding Smart Async Functions For Mobile
Building Smart Async Functions For Mobile
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPress
 
Going fullstack React(ive) - Paulo Lopes - Codemotion Amsterdam 2017
Going fullstack React(ive) - Paulo Lopes - Codemotion Amsterdam 2017Going fullstack React(ive) - Paulo Lopes - Codemotion Amsterdam 2017
Going fullstack React(ive) - Paulo Lopes - Codemotion Amsterdam 2017
 
Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015
 
React 101
React 101React 101
React 101
 
Object-Oriented JavaScript
Object-Oriented JavaScriptObject-Oriented JavaScript
Object-Oriented JavaScript
 
Object-Oriented Javascript
Object-Oriented JavascriptObject-Oriented Javascript
Object-Oriented Javascript
 
Road to react hooks
Road to react hooksRoad to react hooks
Road to react hooks
 
Building complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and ReactBuilding complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and React
 
Creating an Uber Clone - Part XV.pdf
Creating an Uber Clone - Part XV.pdfCreating an Uber Clone - Part XV.pdf
Creating an Uber Clone - Part XV.pdf
 
State manager in Vue.js, from zero to Vuex
State manager in Vue.js, from zero to VuexState manager in Vue.js, from zero to Vuex
State manager in Vue.js, from zero to Vuex
 

Mehr von Astrails

Building and deploying React applications
Building and deploying React applicationsBuilding and deploying React applications
Building and deploying React applicationsAstrails
 
Accounting For Hackers
Accounting For HackersAccounting For Hackers
Accounting For HackersAstrails
 
Machine Learning: Make Your Ruby Code Smarter
Machine Learning: Make Your Ruby Code SmarterMachine Learning: Make Your Ruby Code Smarter
Machine Learning: Make Your Ruby Code SmarterAstrails
 
Engineering esthetics
Engineering estheticsEngineering esthetics
Engineering estheticsAstrails
 
Lean Software Development
Lean Software DevelopmentLean Software Development
Lean Software DevelopmentAstrails
 
RubyMotion: Put your Dreams in Motion with Ruby
RubyMotion: Put your Dreams in Motion with RubyRubyMotion: Put your Dreams in Motion with Ruby
RubyMotion: Put your Dreams in Motion with RubyAstrails
 
WTF is NoSQL
WTF is NoSQLWTF is NoSQL
WTF is NoSQLAstrails
 
Cassandra intro
Cassandra introCassandra intro
Cassandra introAstrails
 
Ruby is an Acceptable Lisp
Ruby is an Acceptable LispRuby is an Acceptable Lisp
Ruby is an Acceptable LispAstrails
 
Ruby is Awesome
Ruby is AwesomeRuby is Awesome
Ruby is AwesomeAstrails
 
Rails missing features
Rails missing featuresRails missing features
Rails missing featuresAstrails
 
Performance - When, What and How
Performance - When, What and HowPerformance - When, What and How
Performance - When, What and HowAstrails
 

Mehr von Astrails (12)

Building and deploying React applications
Building and deploying React applicationsBuilding and deploying React applications
Building and deploying React applications
 
Accounting For Hackers
Accounting For HackersAccounting For Hackers
Accounting For Hackers
 
Machine Learning: Make Your Ruby Code Smarter
Machine Learning: Make Your Ruby Code SmarterMachine Learning: Make Your Ruby Code Smarter
Machine Learning: Make Your Ruby Code Smarter
 
Engineering esthetics
Engineering estheticsEngineering esthetics
Engineering esthetics
 
Lean Software Development
Lean Software DevelopmentLean Software Development
Lean Software Development
 
RubyMotion: Put your Dreams in Motion with Ruby
RubyMotion: Put your Dreams in Motion with RubyRubyMotion: Put your Dreams in Motion with Ruby
RubyMotion: Put your Dreams in Motion with Ruby
 
WTF is NoSQL
WTF is NoSQLWTF is NoSQL
WTF is NoSQL
 
Cassandra intro
Cassandra introCassandra intro
Cassandra intro
 
Ruby is an Acceptable Lisp
Ruby is an Acceptable LispRuby is an Acceptable Lisp
Ruby is an Acceptable Lisp
 
Ruby is Awesome
Ruby is AwesomeRuby is Awesome
Ruby is Awesome
 
Rails missing features
Rails missing featuresRails missing features
Rails missing features
 
Performance - When, What and How
Performance - When, What and HowPerformance - When, What and How
Performance - When, What and How
 

Kürzlich hochgeladen

Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessPixlogix Infotech
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 

Kürzlich hochgeladen (20)

Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 

Migrating from Flux to Redux. Why and how.

  • 1. Migrating from Flux to Redux Boris Nadion boris@astrails.com @borisnadion why and how
  • 6. React - breath of fresh air
  • 13. hold on! is there anything better?
  • 16. each one of them was better …then any other
  • 17. reflux is better than flux because […]
  • 18. Flummox is better than flux because […]
  • 19. alt is better than flux because […]
  • 25. 1 export default class HackerNews extends Component { 2 constructor() { 3 super(...arguments); 4 this.state = HackerNewsStore.getNews(); 5 this.onNewsUpdated = this.onNewsUpdated.bind(this); 6 } 7 12 componentWillMount() { 13 HackerNewsStore.addChangeListener(this.onNewsUpdated); 14 } 15 16 componentWillUnmount() { 17 HackerNewsStore.removeChangeListener(this.onNewsUpdated); 18 } 19 20 onNewsUpdated() { 21 this.setState(HackerNewsStore.getNews()); 22 } 1 export default class Locations extends Component 2 constructor() { 3 super(...arguments); 4 this.state = LocationsStore.getLocations(); 5 this.onLocationsChanged = this.onLocationsCha 6 } 7 8 componentWillMount() { 9 LocationsStore.addChangeListener(this.onLocat 10 } 11 12 componentWillUnmount() { 13 LocationsStore.removeChangeListener(this.onLo 14 } 15 16 onLocationsChanged() { 17 this.setState(LocationsStore.getLocations()); 18 } duplication
  • 26. sync state <- store
  • 27. direct access to actions and store
  • 30. 1 render() { 2 const allYouNeedIsHere = ...; // functions and data 3 return ( 4 <DumpComponent { ...allYouNeedIsHere } /> 5 ); 6 }
  • 31. // const NewsEntry = (props, context) => {}; const NewsEntry = ({ id, title }) => { if (title) { return ( <div> <a href={ `...${id}` } target="_blank">{ title }</a> </div> ); } return ( <div>Loading entry data...</div> ); }; pure function
  • 32. maintain its own state but don’t access actions and store
  • 36. 1 const FormActions = { 2 addLocation: (location) => { 3 handleViewAction({ 4 type: Constants.Form.ADD_LOCATION, 5 location 6 }); 7 RemoteApi.queryTheWeather(location); 8 } 9 }; 1 export const addLocation = (location) => { 2 handleViewAction({ 3 type: Constants.Form.ADD_LOCATION, 4 location 5 }); 6 RemoteApi.queryTheWeather(location); 7 };
  • 37. 1 const LocationActions = { 2 refresh: (location) => { 3 handleViewAction({ type: Constants.Location.REFRESHING, location }); 4 RemoteApi.queryTheWeather(location); 5 }, 6 7 querySuccess: (data, location) => { 8 handleServerAction({ 9 type: Constants.Location.QUERY_SUCCESS, 10 data, 11 location 12 }); 13 }, 14 15 queryFailed: (error) => { 16 handleServerAction({ 17 type: Constants.Location.QUERY_FAILED, 18 error 19 }); 20 }, 21 22 remove: (locationID) => { 23 handleViewAction({ type: Constants.Location.REMOVE, locationID }); 24 } 25 }; 1 export const refresh = (location) => { 2 handleViewAction({ type: Constants.Location.RE 3 RemoteApi.queryTheWeather(location); 4 }; 5 6 export const remove = (locationID) => { 7 handleViewAction({ type: Constants.Location.RE 8 }; 9 10 export const querySuccess = (data, location) => 11 handleServerAction({ 12 type: Constants.Location.QUERY_SUCCESS, 13 data, 14 location 15 }); 16 }; 17 18 export const queryFailed = (error) => { 19 handleServerAction({ 20 type: Constants.Location.QUERY_FAILED, 21 error 22 }); 23 };
  • 39. 1 const Home = () => ( 2 <div className={ classes.home }> 3 <h2>Useless dashboard, flux version</h2> 4 <Form /> 5 <Locations /> 6 <HackerNews /> 7 </div> 8 ); 1 const Home = () => ( 2 <div className={ classes.home }> 3 <h2>Useless dashboard, flux version</h2> 4 <Weather /> 5 <HackerNews /> 6 </div> 7 );
  • 40. 1 import { addLocation } from 'fluxx/actions/form'; 2 import { refresh, remove } from 'fluxx/actions/location'; 3 4 const Weather = () => ( 5 <div> 6 <Form addLocation={ addLocation } /> 7 <Locations remove={ remove } refresh={ refresh } /> 8 </div> 9 );
  • 41. export default class Locations extends Component { static propTypes = { remove: PropTypes.func.isRequired, refresh: PropTypes.func.isRequired }; //… render() { return ( <div className={ classes.locations }> { this.state.locations.map(location => <Location { ...this.props } { ...location } key={ location.uniqId } />) } </div> ); } }
  • 42. import React from 'react'; import classes from './location.sass'; const Location = ({ remove, refresh, status, temperature, icon_url: iconUrl, text, location, uniqId }) => { const removeLocation = (e) => { e.preventDefault(); remove(uniqId); }; const refreshLocation = (e) => { /* … */ }; const renderState = () => { /* … */ }; return ( <div className={ classes.location }> <a className={ classes.remove } href="#" onClick={ removeLocation }>&times;</a> <div className={ classes.name }> { location } </div> { renderState() } </div> ); }; export default Location;
  • 43. 1 export default class HackerNews extends Component { 2 constructor() { 3 super(...arguments); 4 this.state = HackerNewsStore.getNews(); 5 this.onNewsUpdated = this.onNewsUpdated.bind(this); 6 } 7 12 componentWillMount() { 13 HackerNewsStore.addChangeListener(this.onNewsUpdated); 14 } 15 16 componentWillUnmount() { 17 HackerNewsStore.removeChangeListener(this.onNewsUpdated); 18 } 19 20 onNewsUpdated() { 21 this.setState(HackerNewsStore.getNews()); 22 } 1 export default class Locations extends Component 2 constructor() { 3 super(...arguments); 4 this.state = LocationsStore.getLocations(); 5 this.onLocationsChanged = this.onLocationsCha 6 } 7 8 componentWillMount() { 9 LocationsStore.addChangeListener(this.onLocat 10 } 11 12 componentWillUnmount() { 13 LocationsStore.removeChangeListener(this.onLo 14 } 15 16 onLocationsChanged() { 17 this.setState(LocationsStore.getLocations()); 18 } this shit
  • 46. Mixins Are Dead. Long Live Composition https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order- components-94a0d2f9e750
  • 47. react-redux aware of components and flow control
  • 48. redux single source of truth state is read-only changes are made with pure functions x!
  • 52. npm i redux --save npm i react-redux —save npm i redux-thunk —save npm i redux-logger --save
  • 55. containers aware of redux, have no own React code
  • 56. container = smart component = dumb (less smart)
  • 57. refactoring plan api -> better api ;-) actions -> actions stores -> reducers components & containers
  • 58. API
  • 59. import { querySuccess, queryFailed } from 'fluxx/actions/location'; queryTheWeather: (location) => { SuperAgent.get(`…${location}&APPID=${APIKEY}`).set(‘Accept’, 'application/json'). end((error, res) => { if (isSuccess(res)) { querySuccess(res.body, location); } else { queryFailed(res); } }); }, queryTheWeather: ({ {location}, onSuccess, onFailure }) => { SuperAgent.get(`…${location}&APPID=${APIKEY}`).set(‘Accept’, 'application/json'). end((error, res) => { if (isSuccess(res)) { onSuccess(res.body); } else { onFailure(res); } }); }, not aware of actions
  • 61. const querySuccess = (data, location) => ({ type: Constants.Location.QUERY_SUCCESS, data, loca const queryFailed = (error) => ({ type: Constants.Location.QUERY_FAILED, error }); const startAddingLocation = (location) => ({ type: Constants.Form.ADD_LOCATION, location }); export const addLocation = (location) => (dispatch) => { dispatch(startAddingLocation(location)); RemoteApi.queryTheWeather({ payload: { location }, onSuccess: (data) => { dispatch(querySuccess(data, location)); }, onFailure: (error) => { dispatch(queryFailed(error)); } }); }; for each async action, usually there are 3 sync actions: starting the request (update the UI) request succeed request failed
  • 63.
  • 64. const locations = createReducers(DEFAULT_STATE, { [Constants.Form.ADD_LOCATION]: (state, action) => { const { location } = action; // FIXME: yeah, need a better uniq id return [...state, { location, uniqId: location, status: LOOKING_OUTSIDE }]; }, [Constants.Location.QUERY_SUCCESS]: (state, action) => updateState(state, acti [Constants.Location.REFRESHING]: (state, action) => updateState(state, action. [Constants.Location.REMOVE]: (state, action) => removeLocation(state, action.l // so what, do your own error handling ;-) [Constants.Location.QUERY_FAILED]: (state) => state }); switch (action.type) { case Constants.Form.ADD_LOCATION: // FIXME: yeah, need a better uniq id locations.push({ location: action.location, status: "Looking outside...", uniqId: action.location }); return LocationsStore.emitChange(); case Constants.Location.QUERY_SUCCESS: updateLocation(action.location, action.data); return saveChanges(); case Constants.Location.REMOVE: removeLocation(action.locationID); return saveChanges(); case Constants.Location.REFRESHING: updateState(action.location, "Refreshing data..."); return saveChanges(); default: return true; }
  • 65. switch (action.type) { case Constants.HackerNews.TOP_IDS_LOADED: newsIds = action.ids; return HackerNewsStore.emitChange(); case Constants.HackerNews.STORY_FETCHED: news[action.id] = action.data; return HackerNewsStore.emitChange(); default: return true; } const hackerNews = createReducers(DEFAULT_STATE, { [Constants.HackerNews.TOP_IDS_LOADED]: (state, action) => ({ ...state, newsIds: action.ids }), [Constants.HackerNews.STORY_FETCHED]: (state, action) => ({ ...state, news: { ...state.news, [action.id]: action.data } }) });
  • 67. const allowDebugging = (process.env.NODE_ENV !== 'production') || (localStorage && localStorage.getItem('reactDebug') === 'yes'); export const buildStore = () => { const rootReducer = combineReducers({ locations, hackerNews }); const devToolsExt = (allowDebugging && window.devToolsExtension) ? window.devToolsExtension() : f => f; const middleWare = allowDebugging ? applyMiddleware(thunkMiddleware, createLogger()) : applyMiddleware(thunkMiddleware); const store = createStore( rootReducer, undefined, compose(middleWare, devToolsExt) ); return store; };
  • 69. import HackerNews from 'containers/hacker_news'; import Weather from 'containers/weather'; const store = buildStore(); const Home = () => ( <Provider store={ store }> <div className={ classes.home }> <h2>Useless dashboard, redux version</h2> <Weather /> <HackerNews /> </div> </Provider> );
  • 70. import { addLocation, refresh, remove } from 'reduxx/actions/locations'; import Weather from 'components/weather'; import { connect } from 'react-redux'; const mapDispatchToProps = (dispatch) => ({ addLocation: (location) => dispatch(addLocation(location)), refresh: (location) => dispatch(refresh(location)), remove: (location) => dispatch(remove(location)) }); const mapStateToProps = (state) => ({ locations: state.locations }); export default connect(mapStateToProps, mapDispatchToProps)(Weather); container component import React, { PropTypes } from 'react'; import Form from 'components/form'; import Locations from 'components/locations'; const Weather = ({ addLocation, refresh, remove, locations }) => ( <div> <Form addLocation={ addLocation } /> <Locations remove={ remove } refresh={ refresh } locations={ locations } /> </div> );
  • 72. ▾ src/ ▾ components/ ▾ hacker_news/ hacker_news.sass index.js ▾ home/ home.sass index.js ▾ location/ index.js location.sass ▾ locations/ index.js locations.sass form.js news_entry.js weather.js ▾ containers/ hacker_news.js weather.js ▾ src/ ▾ components/ form.js hacker_news.js hacker_news.sass home.js home.sass location.js location.sass locations.js locations.sass news_entry.js move files to folders - less mess
  • 75. const Location = ({ remove, refresh, status, temperature, icon_url: iconUrl, text, location, uniqId }) => { //… }; Location.propTypes = { remove: PropTypes.func.isRequired, refresh: PropTypes.func.isRequired, status: PropTypes.string.isRequired, temperature: PropTypes.number, icon_url: PropTypes.string, text: PropTypes.string, location: PropTypes.string.isRequired, uniqId: PropTypes.string.isRequired }; export default class HackerNews extends Component { static propTypes = { hackerNews: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.number.isRequired, title: PropTypes.string })), fetch: PropTypes.func.isRequired }; //… }