-
-
Save laere/9485b4d02ad88692b8d4 to your computer and use it in GitHub Desktop.
export const AddTodo = (text) => { | |
const post = axios.post(API, { text }); | |
//logging purposes | |
post.then(function(response) { | |
return console.log(response); | |
}, function(err) { | |
console.log('Error') | |
return { | |
type: REMOVE_TODO, | |
id | |
}; | |
}); | |
return { | |
type: ADD_TODO, | |
id: nextId++, | |
payload: text | |
}; | |
}; |
import React, { Component } from 'react'; | |
import ListItem from '../components/ListItem'; | |
export default class List extends Component { | |
constructor(props) { | |
super(props); | |
//reference these functions | |
this.handleOnClick = this.handleOnClick.bind(this); | |
this.clearInput = this.clearInput.bind(this); | |
} | |
handleOnClick(e) { | |
e.preventDefault(); | |
//save input value | |
let inputValue = this.refs.inputfield.value; | |
if(inputValue === '') return; | |
//pass input value to callback | |
this.props.addTodo(inputValue); | |
} | |
clearInput() { | |
//clears input on clear button | |
this.refs.inputfield.value = ''; | |
console.log('This clears the input value'); | |
} | |
renderTodos() { | |
//if items is passed as props | |
if(this.props.items) { | |
//map each item to have item id and item text | |
return this.props.items.map((item) => { | |
return ( | |
//pass removeTodo func to child pass text to child | |
<ListItem removeTodo={this.props.removeTodo} key={item.id} text={item.text} /> | |
); | |
}); | |
} | |
} | |
render() { | |
return ( | |
<div> | |
{/*initiate callback on submit*/} | |
<form onSubmit={this.handleOnClick}> | |
<input type="text" ref="inputfield"/> | |
<input type="submit" value="Add" className="btn btn-primary" /> | |
<input onClick={this.clearInput} type="submit" value="Clear" className="btn btn-primary" /> | |
</form> | |
<ul> | |
{/*render todos*/} | |
{this.renderTodos()} | |
</ul> | |
</div> | |
); | |
} | |
} |
import React, { Component } from 'react'; | |
import List from '../containers/List'; | |
//action creators | |
import { AddTodo, RemoveTodo, FetchTodos } from '../reducers/reducer_todos'; | |
//reference to store | |
import { connect } from 'react-redux'; | |
//allows use of action creators directly (without dispatch wrapper) | |
import { bindActionCreators } from 'redux'; | |
class App extends Component { | |
constructor(props) { | |
super(props); | |
//reference these functions | |
this.addTodo = this.addTodo.bind(this); | |
this.removeTodo = this.removeTodo.bind(this); | |
} | |
componentWillMount() { | |
// fetch todos | |
//then dispatch them to action creator | |
this.props.FetchTodos(); | |
} | |
addTodo(text) { | |
//add to do | |
this.props.AddTodo(text); | |
console.log('This is the text passed to the AddTodo AC: ' + text); | |
} | |
removeTodo(id, e) { | |
//remove todo | |
this.props.RemoveTodo(id); | |
console.log('This is the ID of the removed todo: ' + id); | |
console.log(e.type, e.which, e.timeStamp); | |
} | |
render() { | |
return ( | |
<div> | |
<h1>Todo List</h1> | |
{/*pass down action creators | |
pass down items state*/} | |
<List | |
items={this.props.items} | |
addTodo={this.addTodo} | |
removeTodo={this.removeTodo} | |
/> | |
</div> | |
); | |
} | |
} | |
let mapDispatchToProps = (dispatch) => { | |
return bindActionCreators( | |
{ | |
AddTodo: AddTodo, | |
RemoveTodo: RemoveTodo, | |
FetchTodos: FetchTodos | |
}, dispatch); | |
}; | |
let mapStateToProps = (state) => { | |
return { | |
items: state.items, | |
text: state.text | |
}; | |
}; | |
export default connect(mapStateToProps, mapDispatchToProps)(App); |
You can use both!
The best feature of middleware is that it’s composable in a chain. You can use multiple independent third-party middleware in a single project.
It provides a third-party extension point between dispatching an action, and the moment it reaches the reducer. People use Redux middleware for logging, crash reporting, talking to an asynchronous API, routing, and more.
Be careful though because the order you put them in will matter. Since they are chained each middleware will receive the result of the previous middleware.
In your scenario I would put thunk first, then promise middleware. Thunk will allow to dispatch at will, and then if the object you return is in the form accepted for the promise middleware, it will utilize that.
Take a look at the source for redux-promise (it's just 1 file!)
import { isFSA } from 'flux-standard-action';
function isPromise(val) {
return val && typeof val.then === 'function';
}
export default function promiseMiddleware({ dispatch }) {
return next => action => {
if (!isFSA(action)) {
return isPromise(action)
? action.then(dispatch)
: next(action);
}
return isPromise(action.payload)
? action.payload.then(
result => dispatch({ ...action, payload: result }),
error => dispatch({ ...action, payload: error, error: true })
)
: next(action);
};
}
All it does:
- Check to see if the passed action is in FSA form
- If yes, checks if
payload
is a promise - If yes, executes the promise and when the promise is resolved, executes
dispatch
from thethen()
block.
If it's not a promise then it simply passes the action on to the next thing with next(action)
, but if it it waits to resolve and then dispatches a new action (which the next middleware/thing receives). If there is another middleware in order after it then that receives action
, if not then the reducer gets the action and does its thing.
If you're getting more familiar with promises and how redux flows you should notice that redux-promise is super similar to both what you're already doing with axios and most of the examples I gave on reddit. It's removing the boilerplate you would need to write to handle the promise in each action creator and instead allows you to write an action with a payload
that is a promise
. Then it takes care of executing the promise and dispatching actions depending on the result for you.
Also take a look at redux-promise-middleware as it accomplishes the same thing but with a slightly different approach to how it handles the result of a resolved promise. (I prefer redux-promise-middleware).
As an example of using multiple middlewares -- in the app I am building I have this middleware stack (in order):
- clientMiddleware -- My own custom middleware that combines redux-promise-middleware and thunk-middleware with the middleware example I cited in one of the reddit posts (the one from the boilerplate). It lets me thunk, resolve and dispatch a promise, but also return that promise from the action creator so I can do server-side rendering.
- storageMiddleware -- Let's me store some parts of my app state to local storage (in the browser) so I can persist some UI preferences.
- notificationMiddleware -- A small middleware I built that uses re-notif and listens for actions with a
_REJECTED
type suffix and then attempts to parse an error from the payload and show a toastr notification for a failed api response. (Remember, this happens afterclientMiddleware
so I've already got my api response!) - reduxRouterMiddleware -- Uses react-router-redux to handle route transitions.
Wow awesome, I will definitely use both thunk and redux-promise-middleware in the future. Thanks again for the help, I learned an absolute ton more about redux and other things, and really feel confident using it again in the future!
Wow awesome, alright, one more thing is would thunk replace the promise middleware I am using, or can I use both?