Full-Stack Dev at Movio
@nicmaquet
github.com/nmaquet
https://nmaquet.github.io/redux-akljs-talk
http://movio.co/blog
<ReduxIntro>
const store = Redux.createStore(…);
store.getState();
const Todo = ({ id, text, completed }) => (
<p
style={{textDecoration: completed ? 'line-through' : 'none'}}
onClick={() => store.dispatch({ type: 'TOGGLE_TODO', id })}
>
{text}
</p>
)
function render() {
ReactDOM.render(
<App state={store.getState()} />,
document.getElementById('app')
);
}
render();
store.subscribe(render);
const initialState = … ;
function reducer (state, action) {
if (state === undefined) {
return initialState;
}
switch (action.type) {
… // return newly computed state
}
}
const store = Redux.createStore(reducer);
source: http://makeitopen.com/tutorials/building-the-f8-app/data/
Awesome resources by Dan Abramov (the Redux creator):
http://redux.js.org/docs/introduction/index.html
https://egghead.io/courses/getting-started-with-redux
https://egghead.io/courses/building-react-applications-with-idiomatic-redux
</ReduxIntro>
{
"todo1": {
title: "learn React",
completed: true
},
"todo2": {
title: "learn Redux",
completed: true
},
"todo3": {
title: "write blog post",
completed: false
}
}
{
"type": "object",
"patternProperties": {
"^todo[0-9]+$": {
"type": "object",
"properties": {
"title": { "type": "string" },
"completed": { "type": "boolean" }
},
"required": [ "title", "completed" ],
"additionalProperties": false
}
},
"additionalProperties": false
}
http://json-schema.org/
https://spacetelescope.github.io/understanding-json-schema/
http://www.jsonschemavalidator.net/
https://github.com/tdegrunt/jsonschema
npm install --save jsonschema
import invariant from 'invariant';
import { Validator } from 'jsonschema';
import stateSchema from 'json!stateSchema.json';
const validator = new Validator();
export function validateState(state) {
if (__DEV__) {
const validation = validator.validate(obj, stateSchema);
invariant(validation.valid, `invalid state: ${validation}`);
}
return state;
}
import validateState from './validateState';
export function reducer(state, action) {
return validateState(
…
);
}
function checkState() {
// check for orphaned data
// check for inconsistent data
…
}
export default function checkStateMiddleware({ getState }) {
return (next) => (action) => {
const returnValue = next(action);
checkState(getState());
return returnValue;
};
};
const catchExceptions = (getStore) => (reducer) => (state, action) => {
try {
return reducer(state, action);
} catch (e) {
if (state === undefined) {
// don't dispatch if exception occurs during initialization
throw e;
}
console.error(e);
// use setTimeout to avoid recursive call to dispatch()
setTimeout(() =>
getStore().dispatch({type: 'REDUCER_ERROR', exception: e })
);
return state;
}
}
const store = Redux.createStore(catchExceptions(() => store)(
Redux.combineReducers({
foo: fooReducer,
bar: barReducer,
…
})
));
http://redux.js.org/docs/advanced/Middleware.html
export default function alertMiddleware({ getState }) {
return (next) => (action) => {
switch(action.type) {
case "SAVE_SUCCEEDED":
Alert.success('Todos saved');
break;
case "SAVE_FAILED":
Alert.error(`Could not save todos: ${action.error}`);
break;
case "REDUCER_ERROR":
Alert.error(`Unexpected error: ${action.error}`);
break;
}
return next(action);
};
};
const trackedActionTypes = [
"SAVE_TODOS",
"COMPLETE_TODO",
"CREATE_TODO"
];
export default function heapAnalyticsMiddleware({ getState }) {
return (next) => (action) => {
if (_.includes(trackedActionTypes, action.type) {
heap.track(action.type);
}
return next(action);
};
};