-
Notifications
You must be signed in to change notification settings - Fork 0
React Redux Easy App
1. Node js intallation:
node -v
//if shows version than ok if not
`sudo apt-get update´
curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -
//setup_versionNumber
´sudo apt-get install nodejs`
2. Npm install (normally its installed with nodejs)
npm -v
npm i -g npm
//if nodejs is not installed
3. Create a directory like react-redux-fetch or anything you like and inside that directory do this:
create-react-app .
// if u are not inside client then: create-react-app DirectoryName/.
// if react not installed then
npm i -g create-react-app
//install react globally
So now we have installed our new React APP where we will use Redux and Fetch data from API and show them and also post them.;) cool. lets install redux and react-redux to connect redux with react.
npm i --save redux react-redux
Start your coding part now: (happy hacking 😋 )
**Step 01: **
delete unnecessary logo, css etc from react if you dont need it (optional)
**Step 02: **
Create a component inside src in component directory as src/components/Posts.js
Put this data inside Posts.js.
import React, { Component } from 'react';
class Posts extends Component {
render() {
return (
<div>
Posts Data from Fetch API here by calling this.props.fetchposts() where fetchposts() is a action
</div>
);
}
}
export default Posts;
add this Posts component inside your App.js inside render method: <Posts />
Answer me why In below code we import React and then { Component} in different way???
Answer:
React is a Default export and Component is a Named export. Means 1 module can have 1 default but many named export and default one you can give any name but named can not.
As example:
import React from 'react' // you can give any name for React as it is default export
import {Component} from 'react' // Component is named export and fixed name
Now why Redux important as a short example: you have a state which keeps clickInfo as you click on the button. How would you track them?
var state = {
clickInfo: '0',
}
When clicked or updates:
var state.clickInfo = '1'
So, when you have states that you can not predict or control or send easily one components to another you need something to handle them for you. Redux one of them like flux. It is library that can help us tracking the state. Redux holds up the state within a single location. Also with Redux the logic for fetching and managing the state lives outside React. In the below a complete structure of how redux works in one file which we will split later.
//our store
import { createStore } from 'redux';
//our reducer
const reducerName = (state, action) => {
switch (action.type) {
case "DO_SOMETHING": //here will be action types
state = state + action.payload; // get new updated state from previous state + new action data
break;
case "DO_OTHER_ACTION":
break;
}
return state; //new state
}
const store = createStore(reducerName, someValue);
// here we create store using reducer data and someValue which is initial state value, it could 1 , 2 or any string or object etc.
store.subscribe( () => { // this will show us the result or what we have in store on action call
console.log('Updated Store Data:', store.getState());
});
// dispatch will dispatch the action from its type and send payload data to the reducers
// this part i will do from action creators from later
store.dispatch({
type: "DO_SOMETHING", // type of action to find in reducer
payload: data_from_action
//this value will go to our reducer and we can have this payload data from this DO_SOMETHING action
});
Now give some real data and check this in console:
import { createStore } from 'redux';
const reducerName = (state, action) => {
switch (action.type) {
case "ADD_NUMBER":
state = state + action.payload; // get new updated state from previous state(5) + new action data(95)
break;
}
return state; //new state = 5+95 = 100
}
const store = createStore(reducerName, 5); // 5 is inititalState here
store.subscribe( () => { // this will show us the result or what we have in store on action call
console.log('Updated Store Data:', store.getState());
});
// This dispatch will dispatch the action from its type and send payload data to the reducers
// this part i will do from action creators from later
store.dispatch({
type: "ADD_NUMBER", // type of action to find in reducer
payload: 95 //this number will send to reducer
});
RESULT in console:
Updated Store Data: 100
Lets separate all parts of redux as store, reducers and action files:
Step 03: The Store (the bookstore lol)
create a file first as: src/store.js
You can give any name you like. Store is easy to understand as we are making a Store to collect our states from it. Store will have all reducers(which will be our librarian who takes book for us) in it.
To create a Store we need import createStore method from redux. See here:
import { createStore } from "redux"; // we import redux method createStore
const store = createStore(someReducer); //it can take 1 reducer to create the store
export default store;
Now we will create a reducer for our store. Lets create a js file reducers/index.js
and it will be root of all reducers later.
A reducer is just a Javascript function. A reducer takes two parameters: the current state and an action. In plain React the local state changes in place with setState. In Redux you cannot do that. It will create state for you and update them.
** example simple reducer taking state and action: create this src/reducers/index.js
const rootReducer = (state, action) => state; // I call it rootReducer which will have all reducers in it in future
export default rootReducer;
Now Update your store.js file as below code:
import { createStore } from "redux";
// we create a reducers/index.js file as common reducer file and give a name for it as rootReducer means a root for all reducers, but now we do not have it yet.
import rootReducer from "./reducers/index";
const store = createStore(rootReducer); // our createStore() takes rootReducer in it and create a store for us.
export default store; // finally we export our store, which does not have anything yet
Here the state comes from reducers which we will store.
Step 04: The reducer(state,action) (who create the books/state)
Now we will update our reducer little bit from the upper code which has initial state and items array in it which is empty yet. We will keep our fetched value here soon.
** example simple reducer taking the initial state and actions: create this src/reducers/index.js
const initialState = {
items: []
};
const rootReducer = (state = initialState, action) => state; // return new state as state
export default rootReducer;
What this passing? nothing just empty array.
Only way to change the state is by sending a signal to the store.This signal is an action. “Dispatching an action” is the process of sending out a signal.
Step 05: The Actions (buy/take/read the book)
Now, how do you change an immutable state? You won’t. The resulting state is a copy of the current state plus the new data. Redux Action will do this for us.
Redux actions are nothing more than Javascript objects
Now create actions as: src/actions/postActions.js
// this post action will have all actions creator function or JavaScript function as action to do some cool stuffs for reducer.
Initially our postAction is like below:
I will import this postAction and its fetchPosts() method in my Posts component, Posts component will have always updated data from this method by using FETCH_POSTS action and then send this data as payload to reducer.
export const fetchPosts = posts => ({
type: "FETCH_POSTS", // action type which must be unique name and capital letter to avoid duplication
payload: posts // payload is something where we will keep data or info, you could give any name like data/myValue
});
Here, **type **is just a String with capital letter. The reducer will use that string to determine how to calculate the next state. For String typos and duplicates problem, it’s better to have action types declared as constants.
Step 06: Actions Types (this step u can skip if u do not like types.js file or more files
create a file src/actions/types.js
// i create a separate file for actions type for simplicity which is not mandatory
export const FETCH_POSTS = 'FETCH_POSTS';
Ok, now I can import this types.js into my postAction(src/actions/postActions.js) as:
import { FETCH_POSTS } from '../actions/types';
export const fetchPosts = posts => ({
type: FETCH_POSTS, // action type which must be unique name and capital letter to avoid duplication
// type: "FETCH_POSTS", //If you do not like to use separate types.js
payload: posts // payload is something where we will keep data or info, you could give any name like data/myValue
});
Now update Posts Component with this actions method as:
...
import { fetchPosts } from '../actions/postActions';
class Posts extends Component {
componentWillMount() { // a life cycle method of react
this.props.fetchPosts(); // get properties from fetchPosts method from postActions.js
}
render() {
const postItems = this.props.posts.map(post => ( //this posts data from reducers, then map them based on json data
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.body}</p>
</div>
));
return (
<div>
<h1>Posts</h1>
{postItems}
</div>
);
}
}
....
export default Posts;
We still do not retrieve data from the API in fetchPosts() method. Lets do this and then send back those data to reducer as payload:
export const fetchPosts = () => dispatch => {
fetch('https://jsonplaceholder.typicode.com/posts') // fetch api data
.then(res => res.json()) // response data as json
.then(posts => //get posts and send them to dispatch()
dispatch({
type: "FETCH_POSTS", //action
payload: posts //data to send in reducer
})
);
};
Lets check what will reducer do now.
Create a new reducer file for postActions as src/reducers/postReducers.js
// create some initial states for the reducer
const initialState = {
items: [] // a array of empty state cause we are getting posts of array json data
};
export default function(state = initialState, action) {
switch (action.type) {
case "FETCH_POSTS":
return {
...state, // rest parameter so many parameter can come or all parameter from the sate will come
items: action.payload // data from action
};
default:
return state; //if nothing show default state
}
}
But this reducer is not connected to store yet. So, How will components get data from store. I could import and use this postReducer in my store.js. But what if i want many reducers. I can do this by calling all reducers and combine them to create my store as:
import { combineReducers } from 'redux'; // a redux method to combine reducers
import reducer1 from './reducer1';
import reducer2 from './reducer2';
const store = createStore(combineReducers(reducer1, reducer2));
But I will do much better way. I will use reducers/index.js that we created before to keep all reducers meetup place and then I do not need to import all reducers in my store. Store.js will be untouched.
import { combineReducers } from 'redux';
import postReducer from './postReducer';
export default combineReducers({
posts: postReducer
});
Step 07: React-redux and connect
Connect from react-redux:
- the mapStateToProps function: connects a part of the Redux state to the props of a React component. I will map my states here.
const mapStateToProps = state => ({
posts: state.posts.items
});
- the mapDispatchToProps function: connects Redux actions to React props. This way a connected React component will be able to dispatch actions. I can use my actions method here directly like fetchPosts.
const mapDispatchToProps = (dispatch) => {
return {
fetchPost: (posts) => {
dispatch({
type: 'FETCH_POSTS',
payload: posts
});
};
};
};
I will Use connect() method from react-redux so my App.js and other components will be connected to this prop. This connect function can take mapStateToProps, mapDispatchToProps as parameter.
export default connect(mapStateToProps, mapDispatchToProps)(Posts);
Here I will use { fetchPosts } instead of mapDispatchToProps to reduce code.
...
import { connect } from 'react-redux';
...
import { fetchPosts } from '../actions/postActions';
class Posts extends Component {
componentWillMount() {
this.props.fetchPosts(); //get posts properties from fetchPosts method of postActions
}
.......
}
const mapStateToProps = state => ({
posts: state.posts.items,
});
export default connect(mapStateToProps, { fetchPosts })(Posts);
Step 08: Provider (librarian or postman or Wrapper) To start off connecting Redux with React using Provider. Provider wraps up your React application and makes it aware of the entire Redux’s store. Redux the store manages everything. React must talk to the store for accessing the state and dispatching actions.
Do like this: src/App.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import Posts from './components/Posts';
import store from './store';
class App extends Component {
render() {
return (
<Provider store={store}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Redux-React Test Example by Ariful</h1>
</header>
<hr />
<Posts />
</div>
</Provider>
);
}
}
export default App;
Lets work on the Store again for final changes. I need middleware in my store called applyMiddleware from redux. The function applyMiddleware() can take multiple middleware without depends on one another like applyMiddleware(middleware1, middleware2, middlewareN)
I can use ...middleware argument to take any as applyMiddleware(...middleware). In this store i will use redux-thunk as middleware. redux-thunk will help for async call of function as example to use promises call. For more detail of redux-thunk
Now question is how i can use many middlewares in my create store. Answer is to use compose() from redux. It Composes functions from right to left. So now check my store.js final file
src/store.js
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware)
)
);
export default store;
I like to use redux Dev tools in my crome to see my states and other stuffs. For this I need to use one more like in store.js. You can skip if u like
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
src/components/Posts.js
Now check the Posts component and changes:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchPosts } from '../actions/postActions';
class Posts extends Component {
componentWillMount() {
this.props.fetchPosts();
}
render() {
const postItems = this.props.posts.map(post => (
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.body}</p>
</div>
));
return (
<div>
<h1>Posts</h1>
{postItems}
</div>
);
}
}
const mapStateToProps = state => ({
posts: state.posts.items,
newPost: state.posts.item
});
export default connect(mapStateToProps, { fetchPosts })(Posts);
Lets see postAction file:
src/actions/postActions.js
import { FETCH_POSTS } from './types';
export const fetchPosts = () => dispatch => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => res.json())
.then(posts =>
dispatch({
type: FETCH_POSTS,
payload: posts
})
);
};
Check Reducers:
src/reducers/index.js
import { combineReducers } from 'redux';
import postReducer from './postReducer';
export default combineReducers({
posts: postReducer
});
src/reducers/postReducer.js
import { FETCH_POSTS } from '../actions/types';
const initialState = {
items: []
};
export default function(state = initialState, action) {
switch (action.type) {
case FETCH_POSTS:
return {
...state,
items: action.payload
};
default:
return state;
}
}
So now we will do some design stuffs for cool look ;)
Install this files:
npm i --save reactstrap
npm i --save bootstrap
import 'bootstrap/dist/css/bootstrap.min.css';
// import this to your app.js to all components can have it
Now you will notice little changes on the fonts right? :)
Lets Make another component beside Posts src/components/postForm.js
which can take input as form data and add this at the top of posts. Homework
**Files you can check: **
This is a running tutorial by Ariful, MCE