visit
State. That’s one word behind Redux’s existence in the React framework. But wait a minute what is the state of an application, and why does it matter in single-page applications(SPA). Oh, single-page applications? What are those too? Let’s back it up a bit and take it one step at a time.
This guide assumes you already have a fundamental understanding of React and Redux architecture and API. However, if the opposite is the case you can check out the Redux documentation and the React documentation , as we would focus more on understanding what happens during React-Redux data flow.In the course of this article, we would discuss what single-applications are and what state means in a React context, as sort of a refresher course. Next, we would dive into explaining how Redux plays a role in react, breaking down the various parts that make up the React-Redux flow, then cementing this explanation with various code examples.If you consider the introductory sections of the article quite long and want to quickly head over to the juicy part, you can simply jump over to the subheading titled — Understanding the way Redux works, else stick with me to get a full understanding.
The state can also be referred to as the store in terms of SPAs as seen in the picture above. However, we can define the state of an application as the representation of every piece of data that changes in an application. The state comprises of immutable objects.
The immutability of an application’s state comes with several benefits. An immutable value or object cannot be changed, so every update will create a new value, leaving the old one untouched. For example, if an application’s state is immutable, one can simply save all the state’s objects in a single store to easily implement undo/redo functionality. Think version control system. Here is a code example of immutability:
//Here we can't assign the named instance to a new object,
//but we can change the object it is assigned to. This object is mutable.
const obj = {a: 1, b: 2}
obj.a = 3
console.log(obj.a) // 3
//But if we use Javascript's Object.freeze() method we gain immutablity.
const obj = {a: 1, b: 2}
Object.freeze(obj) // This makes the object immutable.
obj.a = 3 //
console.log(obj.a) // 1
Going forward, simply keep in mind that one essential quality of state is immutability.
What happened was as the temperature of the water changed the state also changed. Pretty straightforward analogy right?
Here is a code example below to drive home the point:Notice that as we toggle the slider from zero(0) unit to its maximum value which is one hundred(100) units, the state of water changed from solid, then to liquid, and finally gaseous above 100 units of temperature.This is what happened to the state of the React component as seen in the code snippet. This shows that by changing the state of a component you change the way it behaves and ultimately what it returns or renders, pretty cool right?…a predictable state container for JavaScript applications. It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test.
Hence as stated at the beginning of this article, the management of the state is the role Redux plays in React. Just state? One could ask. Oh, true it’s simply state management, but don’t get quickly disappointed because the scope and dimension state could take can go from simple to complex as the application grows in magnitude. It’s like planting a seed by the roadside and a few years later when it becomes a large tree you regret not planting it in a park where it would be able to grow to whatever size. Hence, it’s good practice to start a project as you mean to go on, by using the right state management tool so however complex your application gets you will have it under control, under one state. Oh, what is one state?One state means a single source of truth for the entire application. It sounds intriguing, doesn’t it? Just one source of truth would make an application no matter how complex more manageable. Hence the state must be immutable and if there is to be a change it should completely remove the previous state and bring in a new state.So the Redux store is the single source of truth for every React component. This makes things way easier than letting each component manage its state. Hey but to appreciate Redux better, let’s analyze what could happen if each component maintains its state, we’ll do that with an example.In React to share data among siblings, a state has to live in the parent component, and then a method to update the state will exist in the parent component and be passed down to the sibling components as props. So even if the parent component doesn’t need the state it is passing down it would still hold this state. Doesn’t that sound rather superfluous and redundant? Let’s use a simple authentication example to illustrate further.// Auth is a parent component.
const Auth = () => {
const [isLogged, setLogin] = useState(false)
// It holds the state for it's two children components despite not needing the state itself.
// This is the method used to update the state when an action is carried out in a sibling component.
const setLogin = (username, password) => {
if (username === user && password === pass) {
setLogin(true)
}
}
return (
<div>
// Here the state is passed down to the child component as a props and
//updated when an action is carried out in the second child component.
<Status status={isLogged}/>
<Login handleLogin={setLogin}/>
// This child commponent carries out the action that changes the state,
// receiving setLogin method as a props.
</div>
)
}
You would have to pass the state from one component to the next until it gets to where it is needed. Phew, that got terrifyingly complex pretty fast. Hence it is clear that state management could get messy as the application grows. Enter Redux.
Redux eliminates the need to pass data through components that don’t need them, by connecting a component directly to the state shared by the entire application.
Redux consists of three parts — actions, store, and reducers. We will briefly highlight each of these parts explaining the role each plays in Redux. This would prove to be important because you would get a firm understanding of the way Redux works and how to use it.
Actions are sent or dispatched using the store.dispatch() method. They are plain javascript objects, containing two properties; a type property and a payload property. The type indicates the type of action to be carried out, while the payload contains the data to be stored.
Let’s use an analogy of an action that can be carried out when changing the state of water to solid, funny analogy but walk with me:{
type: 'CHANGE TO SOLID',
payload: {
state: solid
}
}
const changeToIce = (solid) => ({
type: 'CHANGE TO SOLID',
payload: {
state: solid
}
})
const waterReducer = (state = initialState, action) => {
switch (action.type) {
case 'CHANGE TO SOLID':
return {...state, action.payload }; //Here action.payload will be solid.
case 'CHANGE TO LIQUID':
return {...state, action.payload}; //Here action.payload will be liquid.
default:
return state // This will return the default state which we would set as gas,
// if any of the above cases don't checkout.
}
}
The store is responsible for holding the state of the application. There is just one store in Redux, hence one source of truth. Apart from holding the application state, the store has , which include; allowing access to the state via ‘store.getState()’, updating the state via ‘store.dispatch(action)’, registering and registering from listeners via ‘store.subscribe()’.
Every action that gets dispatched to the store must return a new state, making the store predictable. To create a store from our water reducer is just a single line:const store = createStore(waterReducer)
But what if we have multiple reducers? Then we could combine them using the ‘combineReducers()’ method in Redux.
// actions/index.js
const ADDBOOK = book => ({
type: "ADD BOOK",
book
});
const REMOVEBOOK = book => ({
type: "DELETE BOOK",
book
});
const FILTER = filter => ({
type: "FILTER",
filter
});
export { ADDBOOK, REMOVEBOOK, FILTER };
//reducers/book.js
const initialState = [
{
name: "The Intelligent Investor",
author: "Ben Graham"
},
{
name: "Harry Potter and the Philosopher's Stone",
author: "J. K. Rowling"
}
];
const addBook = (state = initialState, action) => {
switch (action.type) {
case "ADD BOOK":
return [...state, action.book];
case "DELETE BOOK":
return [...state].filter(bk => bk !== action.book);
default:
return state;
}
};
export default addBook;
//reducers/filter.js
const filter = (state = "", action) => {
switch (action.type) {
case "FILTER":
return action.filter;
default:
return state;
}
};
export default filter;
//src/index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import rootReducer from "./reducers/index";
import App from "./components/App";
const store = createStore(rootReducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
Now you will notice that after creating the store and logging ‘
store.getState()
’ to the console, it outputs a single object that contains what we would call ‘pieces of our one state’.We would add our components and containers next. First, we add the book component://components/book.js
import React from "react";
import PropTypes from "prop-types";
const Book = ({ bk, handleRemove }) => (
<div style={{ border: "1px solid green", padding: "5px" }}>
<h2>{bk.name}</h2>
<h3>{bk.author}</h3>
<button type="button" onClick={() => handleRemove(bk)}>
Delete
</button>
</div>
);
Book.propTypes = {
bk: PropTypes.shape({
name: PropTypes.string,
author: PropTypes.string
}).isRequired
};
export default Book;
Then add the ‘
book list
’ container afterward://containers/bookList.js
import React from "react";
import { CREATEBOOK, REMOVEBOOK, FILTER } from "../actions/index";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import Filter from "../components/filterBook";
import Book from "../components/book";
const mapStateToProps = state => ({
books: state.book,
filter: state.filter
});
const mapDispatchToProps = dispatch => ({
addBook: book => dispatch(CREATEBOOK(book)),
deleteBook: book => dispatch(REMOVEBOOK(book)),
filterInput: string => dispatch(FILTER(string))
});
const BookList = ({ filter, filterInput, books, deleteBook }) => {
const handleRemove = bk => deleteBook(bk);
const handleFiltering = books => {
if (filter !== "" && filter.split("")[filter.length - 1] !== "\\") {
const pattern = new RegExp(`${filter}`, "i");
const filtered = books.filter(book => pattern.test(book.name));
return filtered.map((bk, i) => (
<Book bk={bk} key={bk.name + i} handleRemove={handleRemove} />
));
}
return books.map((bk, i) => (
<Book bk={bk} key={bk.name + i} handleRemove={handleRemove} />
));
};
const handleFilterChange = e => {
filterInput(e.target.value);
};
return (
<div>
<Filter filter={filter} handleFilterChange={handleFilterChange} />
<h1>Books</h1>
{handleFiltering(books)}
</div>
);
};
BookList.propTypes = {
filter: PropTypes.string.isRequired,
filterInput: PropTypes.func.isRequired,
books: PropTypes.arrayOf(Object).isRequired,
deleteBook: PropTypes.func.isRequired
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(BookList);
Now let’s add our ‘
book form
’ container://containers/bookForm.js
import React from "react";
import { connect } from "react-redux";
import { ADDBOOK } from "../actions/index";
import PropTypes from "prop-types";
const mapDispatchToProps = dispatch => ({
createBook: book => dispatch(ADDBOOK(book))
});
const BookForm = ({ createBook }) => {
const handleSubmit = e => {
e.preventDefault();
const formElems = e.target.elements;
const bookName = formElems.bookName.value;
const author = formElems.author.value;
formElems.bookName.value = "";
formElems.author.value = "";
return createBook({ name: bookName, author });
};
return (
<div>
<form onSubmit={handleSubmit}>
<h1>Kindly input the book details</h1>
<label htmlFor="bookName">
Book Name
<input type="text" name="bookName" id="bookName" required />
</label>
<label htmlFor="author">
Book Author
<input type="text" name="author" id="author" required />
</label>
<input type="submit" value="Create Book" />
</form>
</div>
);
};
BookForm.propTypes = {
createBook: PropTypes.func.isRequired
};
export default connect(
null,
mapDispatchToProps
)(BookForm);
//components/filter.js
import React from "react";
import PropTypes from "prop-types";
const Filter = ({ filter, handleFilterChange }) => (
<div>
<h3>Search Available Books</h3>
<input value={filter} type="text" onChange={handleFilterChange} />
</div>
);
Filter.propTypes = {
filter: PropTypes.string.isRequired,
handleFilterChange: PropTypes.func.isRequired
};
export default Filter;
Want to have a better tool in your arsenal to deal with rails' vulnerability? Consider checking out my previous article; Rails Security-Eliminating CSRF and XSS vulnerabilities.
Kindly reach out to me if you have better tips or if you feel I made a mistake here i would be glad to edit this. Thanks a bunch!!!Previously published at