React Data Actions
What
React Data Actions is a tool for helping you get remote data to your components. No more componentDidMount requests. No more setting state.
Out of the box support for show, index, create, delete, update, and invalidation state calls.
It also has use for simple state storage for stuff that does not use a backend.
Why
This helped us remove about 40% of code for certain top-level views. It also prevented multiple requests for data, like if you sidebar needs the list of something and the view itself does too.
How
Install
$ npm i react-data-actions
Setup your DataProvider to give out the DataManager at the application top level
DataManager manages the state of the application. You need to create an instance of this or maybe two if you don't want shared data between applications in the same browser.
DataProvider receives the DataManager and providers it to all the views that need it, so those "ConnectedComponents" know where to get their state from.
# applicationjsconst dataManager = ; ReactDOM;
Create an action generator
Now that our components that we create can access it, lets define where some data is.
Let's make a list of all the Authors
# authorsActionsjs ; path: 'authors' idAttribute: 'id' // its defaulted to id maxAge: 60000 // milliseconds, defautls to a day;
Create an Connected Component
So we have these "authorsActions" we created above. Basically its a generator that can helps us do RESTful calls to that path
# AuthorsListjs ;;; Component static { return authors: authorsActions // key is authors, so will be access this.props.authors } { if thispropsauthorsisFetching // show loader else if thispropsauthorshasError // show errors else const data = thispropsauthorsdata; // the result of the resposse return <ul> ... loop through the data and render authors </ul> ; } AuthorsList;
Most important thing is that connect at the bottom, it will wrap your React.Component and connect it with the DataPovider.
So then what the magic?
So as the requests are happeing, your ConnectedComponent (like AuthorsList) will re-render itself every time it's state changes. So when the request kicks off, it will be rendered, when it succeeds or fails it will re-renders, when another view modifies the state, it will re-render.
Got any more magic?
You can do a default if you do an indexAction(), if the response is an array of models, data-actions-generator will cache every model by the idAttribute (which defaults to 'id') so if you do a showAction({ id: 3 }) and a previously completed indexAction has that ID, it wont do a request for it.
More Examples
Create
# CreateAuthorsFormjs ;;;; Component static propTypes = createAuthor: PropTypesfuncisRequired onComplete: PropTypesfuncisRequired static { return createAuthor: authorsActions } { evt; const name = thisrefsnamevalue; // React 1.4 allows dom elements to not need ReactDOM.findDOMNode if name this; else this; } { return <form onSubmit= ::thisonSubmit > this <input name="name" ref="name" /> <button type="submit">Create Author</button> </form> ; } { if thisstateerrorMessage return <div className="error"> thisstateerrorMessage </div>; } { thisprops; } CreateAuthorsForm;
Update
This will cascade to any other view when its done updating.
# UpdateAuthorFormjs ;;; Component static propTypes = author: PropTypesobjectisRequired // the current other model updateAuthor: PropTypesfuncisRequired // the connected action onComplete: PropTypesfuncisRequired // the onComplete call back static { return updateAuthor: authorsActions } { superprops context; thisstate = name: propsauthorname // i'm handed an author, so lets just use the name ; } { if propsauthor this; } { this; } { evt; if thisstatename this; else this; } { return <form onSubmit= ::thisonSubmit > this <input name="name" ref="authorsName" value= thisstatename /> <button type="submit">Update Author</button> </form> ; } { if thisstateerrorMessage return <div className="error"> thisstateerrorMessage </div>; } { // Since we pass in the entire object for this.props.author, it will read its id attribute and figure where to update it. thisprops
Invalidation
This example is an index invalidation. so assume its a force refresh.
# AuthorsListjs ;;; Component static { return authors: authorsActions forceRefreshAuthors: authorsActions } { // Notice that this page still has a requirement for this data, // so once this invalidation is done, this view __WILL__ refetch the data. thisprops; } { if thispropsauthorsisFetching // show loader return <img src="spinner.gif" /> else if thispropsauthorshasError // show errors return <div className="error"> thispropsauthorserrorMessage </div>; else const data = thispropsauthorsdata; // the result of the resposse return <ul> this </ul> <button onClick= ::thisonButtonClick >FORCE REFRESH ME</button> ; } { return data; } AuthorsList;
Non-backend data
Use actionsGenerator
for simple state stuff. Just don't forget if you are storing an object or array, to clone it or use something immutable.
// dateRangeActions.js ; name: 'blob' // optional maxAge: 30000 // millisecond, optional, by default its forever;
// Page.js ;;; Component static { return setDateRange: dateRangeActions// this.props.setDateRange is a setter function dateRange: dateRangeActions // this.props.dateRange is the state value, not a getter } { return <div> thispropsdateRange </div>; }Page;