Hello React lovers!
In this and the next two articles I will write, I plan to discuss redux and its relationship with react.
I will touch on these concepts before I think it would be difficult to understand Redux without understanding the state management issue and its problems.
The topics that I will cover in this article are shown below.
- 1. What is state management?
- 2. State management problems.
- 3. Technologies that find solutions to problems.
- 4. What is Redux?
- 5. Basic concepts in Redux.
- 6. How Redux works.
- 7. Redux example.
- 8. What is state management?
State is the properties and information that each of the components in our applications currently have. The variability of this feature and information reveals the concept called state.
For example, whether a checkbox is checked or not is a state, an information. We use this information to determine how to move forward in our practice. On the other hand, the location, size, shape, etc. of this checkbox. Since the information is fixed, it would be more logical not to specify it as a state.
State management problems?
As the components inside the application increase, these cases increase. As such, the management of these situations is becoming more and more inextricable. For example, models of states may replace one another, or a case may act on an unrelated component. The most common example of this problem in many places is the problem that Facebook has with its messages and notifications. This problem is uncontrolled data flow. Facebook solves this with its flux architecture. This architecture reduces complexity by providing one-way data flow.
_
Technologies that find solutions to problems.
_
These problems were first solved with the flux architecture. Apart from that, most of the developing technologies have similarities with this architecture. Almost all of them have the same concepts.
Here are a few of them.
- redux
- Akita
- NGRX
- mobx
- React Context
- vuex
- carebral
What is Redux?
It is an open source state management library, almost the most used. If we understood state management, I think we understood what redux actually does. In general, it tries to make state more manageable by centralizing the state and reducing the complexity of the implementation. Redux is created by the head of react (dan abramov). It is a standalone library with React. The reason why it is mentioned so much with React is that it works very compatible with React (see: react-redux). We can use this library in other javascript libraries very easily.
Basic concepts in Redux.
Action: It is a javascript object. It carries the information (type) and state data of which state will change in the store (payload).
{ type: ORDER_INCREASE , payload: 1 }
We often use Action Creators to make Actions more useful. Action Creators are arrow functions. It just takes the changed payload part as a parameter and returns an action. Types are usually kept in a separate file (actionTypes) as they are only directional constants that do not change.
//Action Creator
const orderIncrementSuccess = quantity => ({ type: ORDER_INCREASE, payload: quantity });
//actionTypes.js
export const ORDER_INCREASE = ”ORDER_INCREASE”
export const ORDER_DECREASE = ”ORDER_DECREASE”
Reducer: It is a pure function that takes state and action as parameters and returns the new state. It changes the state according to the action's type and returns the new state. An up-to-date copy of the state must be returned. Otherwise, the components will not render themselves. The purpose of doing this is to change the reference of the state. We can use Object.assign() or Object spread opretaor methods for this. We should not forget to give the initial value so that the state is not undifined at the start of the program.
const reducer=(state=0,action)=>{
switch(action.type){
case 'ORDER_INCREASE':
return state+action.payload;
case 'ORDER_DECREASE':
return state-action.payload;
default:
return state;
}
}
We do not use reducer directly. We send reducers as parameters to the store. When we create the action we send using store,dispatch and the state defined in the store, we pass parameters to the reducer function that we send. As a result of this operation, the returned state is overwritten by the state in the store. So we just tell the store how to change the state using the dispatch function. Store runs the reducer function.
Here is a question that may come to your mind. “Store, which action will be passed to which reducer? How does he determine that?” I thought a lot about this question. The most logical explanation to me is; It does this in all reducers by passing the actionu parameter. Whichever switch is caught, it does the relevant operation there. At least I haven't seen the opposite yet. If friends who know write in the comment section, we will get more accurate information.
Store: This is where the State is kept. We use the createStore function to create the Store. It takes reducers as parameters. When there are more than one reducer, which is usually the case. We use the combineReducer function to send them together. Redux keeps these reducers in a common object for us, making it easy to access and use. Although it does it for itself, we just use it.
Three functions return.
- dispatch
- getState
- subscribe
dispatch: Triggers the reducer by taking the action as parameters. As a result, the state is changed. Every time this function is run, all subscribed components are rendered again. Of course, after the state changes, the rendering is done.
getState: Returns the current state.
subscribe: Components subscribe to the Store using this function. Store stores all subscribed components in it. As a parameter, it takes the function that the component depends on the state, that is, it will render whenever the state changes, it is important that this function uses the getState function. Actually the purpose here is to run getState. In this way, we can see that the state has changed in the view. Otherwise, the state changes, but this is not reflected in the view.
How Redux works;
I will write the steps of the above flow in items.
The user performs an action in the view that will trigger the action.
Dispatch passes the incoming action as a parameter to the reducer with the current state in the store.
As a result of this operation, the relevant parts of the state change and a new state is formed. Here we understand the importance of Object.assign() or Object spread operator methods for reducer, if state holds more than one data.
As the state is updated, all subscribed components are rendered again.
Finally, this state is reflected back to the view.
Redux example
Our example will be a counter instance, as in most examples.
Below are the html codes and images.
<html>
<head>
</head>
<body>
<div id="root">
<h1 id="value"></h1>
<button id="btn_increase">+1</button>
<button id="btn_decrease">-1</button>
</div>
<script src="sampleRedux.js"></script>
</body>
</html>
We will simply increase and decrease the counter. We will keep the value of the counter as state.
Reducer
const counterReducer=(state=0, action)=>{
switch(action.type){
case 'INCREMENT':
return state+1;
case 'DECREMENT':
return state-1;
default:
return state;
}
}
We set the initial value of the reducer to zero. We return the new state according to the type of the incoming action. If there is a type that does not match, we return the current state. Since the action of the incoming type is fixed, the payload feature is not given to the action.
Store
const createStore=(reducer)=>{
let state;
let listeners=[];
const getState=()=>state;
const dispatch=(action)=>{
state=reducer(state ,action);
listeners.forEach(listener=>listener());
};
const subscribe=(listener)=>{
listeners.push(listener);
return()=>{
listener=listener.filter(l=>l!==listener);
};
}
dispatch({});
return {getState, dispatch, subscribe}
}
GetState returns the current state in the Store.
If you notice in the dispatch function, it sends the action, which comes as a parameter with the state defined in the store, to the reducer, which comes as a parameter to our store. It sets the return value to state.
After this process, it renders by browsing through all the subscribed components in listeners. In this way, it is reflected in the current state view.
The subscribe function takes parameters and pushes the components that want to subscribe to the listeners array. Running the loopback function unsubscribes the subscribed component.
Before returning these three functions, we run dispatch once for the state to be created.
const store = createStore(counterReducer);
const render=()=>{
document.getElementById("value").innerText=store.getState();
}
var unSubscribe = store.subscribe(render);
//unSubscribe();
render();
document.getElementById("btn_increase").addEventListener('click',()=>{
store.dispatch({type:'INCREMENT'});
})
document.getElementById("btn_decrease").addEventListener('click',()=>{
store.dispatch({type:'DECREMENT'});
})
First, we create our store by passing the reducer as a parameter. We render the places where the State will be displayed by connecting getState to the relevant components (rendering).
To be notified when the state changes, we subscribe with the store.subscribe function. This function returns a function (unSubscribe). If we run it, we unsubscribe the component.
By running the render function once, we reflect the current state to the component. Zero will appear because its initial value is zero.
Finally, we connect the dispatch methods to the click event of the buttons. Since each button sends a different action, it will have different actions on the state.
While our application is running
HAPPY CODING!
Top comments (1)
Awesome.. Thankyou!