User Tools
development:applications:reactjsredux
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| development:applications:reactjsredux [2017/08/11 16:49] – [React and Redux] 1carew1 | development:applications:reactjsredux [2021/06/25 10:09] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== React and Redux ====== | ||
| + | --- // | ||
| + | Note I do not go into detail about npm or node or webpack or how to install packages. This is purely how to use React with Redux | ||
| + | |||
| + | **NOTE decorators(@) do not work with create-react-app-tool - the reason behind this is that you need to enable the plugin via webpack. I give the smart component both with and without the Decorator** | ||
| + | ===== What is Redux? ===== | ||
| + | Redux is a state container for JavaScript Apps. If you have read the [[development: | ||
| + | |||
| + | Redux manages the state/data of the frontend and it works very well with React. | ||
| + | |||
| + | As a source to watch there are 7 videos explaining how to use React with Redux if you would like a more in depth tutorial : https:// | ||
| + | |||
| + | ===== Why do we need Redux? ===== | ||
| + | My best advice on why we need Redux is build a full application without it. The benefit of that is you will learn a lot about react and how to pass components and data around and pass properties of a parent component down to children component and execute parent functions within children and getting the child of one element to rerender the parent etc. | ||
| + | |||
| + | All of which is very good to know and to know how to do. | ||
| + | |||
| + | The issue you WILL encounter eventually is that you will need to pass data down many many levels to a super great grandchild(not an actual component term :P) component even though all the components inbetween do not need to know about this data. Doing this tightly couples a lot of components together and makes them a lot less reusable as well as over completes your components such that you may have a component that needs like 10+ properties just to work or a developer just can't follow why the data is being passed from this component to this component since they have nothing to do with that specific set of data. | ||
| + | |||
| + | This is where Redux comes in. | ||
| + | |||
| + | ===== The package.json ===== | ||
| + | Before starting this you need a few dependencies so ensure your package.json looks something like : | ||
| + | |||
| + | <code json> | ||
| + | { | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | }, | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ===== Redux ===== | ||
| + | From here on out any component that uses Redux will be called a smart component and anything that does not will be a standard component/ | ||
| + | |||
| + | Think of the redux approach as like One big store (a JavaScript Object) that anything that uses redux has access to. This is a large over simplification but it does allow for some very and clean state management. | ||
| + | Redux when done properly also allows for history so you are able to see what was in the store before an after a change happens. | ||
| + | |||
| + | The main items you want to be concerned with as regard Redux with React are : | ||
| + | * actions | ||
| + | * reducers | ||
| + | * the store | ||
| + | |||
| + | |||
| + | ===== Actions ===== | ||
| + | Actions are the functions you wish to perform when something happens in the UI i.e. application is loaded, button is pressed, text area is populated or altered etc. Actions are what you want to call when an event happens so you take action on the event. | ||
| + | |||
| + | Actions like any other functions can take parameters but the actions should always return a JavaScript Object (or a function that dispatches a JS Object). This JavaScript object must have a member called ' | ||
| + | |||
| + | |||
| + | ===== Reducers ===== | ||
| + | Reducers are functions which must return a state (the object contained within the store). A Point to note here is in the store you will have an entry for each reducer such that if you have 3 reducers called ' | ||
| + | <code javascript> | ||
| + | { | ||
| + | company : {}, | ||
| + | client : {}, | ||
| + | account : {} | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | The reducer should cater for each scenario you send out with an action so usually you pull the type from the incoming state and use this (usually with a case statement - could use a Command Pattern if very large number of types) to determine what to do with the incoming payload i.e. you may have a type of CLIENT_FETCH_FULFILLED and you want to set the state.client to be action.payload (all of this should hopefully make more sense when we have some code examples below). | ||
| + | |||
| + | For every action file you have you should have a reducer such that if you have 3 files clientActions.js, | ||
| + | |||
| + | ===== The Store ===== | ||
| + | The is where the whole state of the application is stored. | ||
| + | The store uses the concept of Middleware, I will not be going into heavy detail on this as the tutorial linked above will give a better explanation but middleware is how the results of the actions get communicated to the reducers and thus alter the state/ | ||
| + | |||
| + | In most cases (depending on how you want it configured of course) a store.js would look like : | ||
| + | |||
| + | <code javascript> | ||
| + | import {applyMiddleware, | ||
| + | |||
| + | import logger from ' | ||
| + | import thunk from ' | ||
| + | import promise from ' | ||
| + | |||
| + | import reducer from ' | ||
| + | |||
| + | const middleware = applyMiddleware(promise(), | ||
| + | export default createStore(reducer, | ||
| + | </ | ||
| + | |||
| + | Note the line <code javascript> | ||
| + | |||
| + | ===== Setup and Example ===== | ||
| + | Now to be honest, overall this is complicated to setup, but once setup it runs very nicely and is easy enough to add on to. | ||
| + | Just remember when there store gets updated this basically the same as calling .setState in all the components, the Virtual DOM then compares the differences and renders them. | ||
| + | |||
| + | Overall you would want your full React + Redux project structure to look something like : | ||
| + | {{ : | ||
| + | |||
| + | The Contents of the store.js is as in 'The Store' section. | ||
| + | |||
| + | ==== index.js ==== | ||
| + | |||
| + | |||
| + | The index.js should contain something like : | ||
| + | <code javascript> | ||
| + | import React from " | ||
| + | import ReactDOM from " | ||
| + | import {Provider} from " | ||
| + | import App from " | ||
| + | import store from " | ||
| + | |||
| + | const rootElement = document.getElementById(' | ||
| + | |||
| + | ReactDOM.render(< | ||
| + | < | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | The main difference between this index.js and the one in the React only tutorial is the additions of the Provider from react-redux which wraps the render of App and the store which is used as a prop for the Provider. | ||
| + | |||
| + | ==== accountActions.js ==== | ||
| + | Now lets make our first action. We will go with the accountActions.js and pretend we are still working with a counter as in the last app just so you can compare the difference between a smart and a dumb component. | ||
| + | |||
| + | <code javascript> | ||
| + | export function incrementCounter(incrementBy) { | ||
| + | return { | ||
| + | type: " | ||
| + | payload: incrementBy | ||
| + | }; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== accountReducer.js ==== | ||
| + | The reducer is where you set the default state of that value in the store so this will look like : | ||
| + | |||
| + | <code javascript> | ||
| + | export default function reducer(state = { | ||
| + | count: 1, | ||
| + | anyOtherValueOrObject : {} | ||
| + | }, action) { | ||
| + | // This is what allows the history by creating a new state that is a clone of the current state | ||
| + | // before modification | ||
| + | state = Object.assign({}, | ||
| + | switch (action.type) { | ||
| + | case " | ||
| + | let counterValue = state.count; | ||
| + | counterValue += action.payload | ||
| + | state.count = counterValue; | ||
| + | break; | ||
| + | } | ||
| + | } | ||
| + | return state; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== ./ | ||
| + | This is where you want to combine all of your reducers together : | ||
| + | |||
| + | <code javascript> | ||
| + | import {combineReducers} from ' | ||
| + | import account from ' | ||
| + | import client from ' | ||
| + | import company from ' | ||
| + | |||
| + | export default combineReducers({ | ||
| + | account, | ||
| + | client, | ||
| + | company | ||
| + | }); | ||
| + | </ | ||
| + | |||
| + | Make sure your reducers are all coded as you want them, if you are not ready to test a reducer yet just remove it from the combineReducers method. | ||
| + | These reducers are then used in with the store so the middleware knows the reducers. | ||
| + | |||
| + | We should now be ready to start making smart component - Note You should try have as few smart components as you can as if everything is a smart component then none of them are. If you can pass data down a level or two and that child isn't doing anything crazy then it probably does not need to be a smart component. | ||
| + | |||
| + | ==== App.js - Smart Version ==== | ||
| + | |||
| + | === Use Decorators - So using Webpack === | ||
| + | |||
| + | <code javascript> | ||
| + | import React, {Component} from ' | ||
| + | import {connect} from " | ||
| + | import * as accountActions from ' | ||
| + | |||
| + | |||
| + | @connect((store) => { | ||
| + | return { | ||
| + | count: store.account.count | ||
| + | }; | ||
| + | }) | ||
| + | class App extends Component { | ||
| + | componentDidMount(){ | ||
| + | this.incresaseCounter(); | ||
| + | } | ||
| + | |||
| + | incresaseCounter() { | ||
| + | this.props.dispatch(accountActions.incrementCounter(1)); | ||
| + | } | ||
| + | |||
| + | render() { | ||
| + | return ( | ||
| + | <div> | ||
| + | < | ||
| + | <button type=" | ||
| + | </ | ||
| + | ); | ||
| + | } | ||
| + | } | ||
| + | export default App; | ||
| + | </ | ||
| + | |||
| + | === Without using Decorators - create-react-app === | ||
| + | |||
| + | <code javascript> | ||
| + | import React, {Component} from ' | ||
| + | import {connect} from " | ||
| + | import * as accountActions from ' | ||
| + | |||
| + | |||
| + | class App extends Component { | ||
| + | componentDidMount(){ | ||
| + | this.incresaseCounter(); | ||
| + | } | ||
| + | |||
| + | incresaseCounter() { | ||
| + | this.props.dispatch(accountActions.incrementCounter(1)); | ||
| + | } | ||
| + | |||
| + | render() { | ||
| + | return ( | ||
| + | <div> | ||
| + | < | ||
| + | <button type=" | ||
| + | </ | ||
| + | ); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | function mapStateToProps(store) { | ||
| + | return { count: store.account.count }; | ||
| + | } | ||
| + | |||
| + | export default connect(mapStateToProps)(App); | ||
| + | </ | ||
| + | |||
| + | That should be everything you require. If you're getting issues with the reducer index.js remember to remove the unused reducers. | ||
| + | |||
| + | The example of this project without any npm or webpack can be found in Dropbox/ | ||
| + | |||
| + | ===== Solution ===== | ||
| + | For a sample solution please go to : Dropbox/ | ||
| + | |||