What is Redux?
It’s a front-end library used for state management in Javascript applications. Moreover it’s a way of organizing your data flow so you can have access to resources all over the application in a easier way than using props to transfer data from class to class.
How to use it?
We need to have three main elements.
Actions
As the name suggests it’s used to describe and dispatch characteristics and data for the actions to be taken such as action type and payload to the store.
Store
Store makes the connection between action and reducer and maintains, stores the needed resources(application state) to be shared.
Reducers
The actions characteristics are sent here and the reducer implements the behavior described by the actions.
Provider
Makes the store visible to all the components that are inside it.
Connect
It’s used by the component to have access to the state from store.
First argument it’s mapStateToProps. This function bind the needed data from the store. The second argument it’s mapDispatchToProps used to bind the functions.
Redux thunk
Redux Thunk is a middleware that lets you call action creators that return a function instead of an object.
Middleware is some code you can put between the request and the response used for a variety of things, including asynchronous API calls.
Let’s put them all together
We have a simple form with an input and a button and we want to use redux to access and transfer data.
Action.js
export const REQUEST_LOADING = "REQUEST_LOADING";
export const GET_REQUEST = "GET_REQUEST";
export function getRequest(id) {
return dispatch => {
dispatch({
type: REQUEST_LOADING,
payload: "123"
});
};
}
export function postRequest(form) {
return dispatch => {
dispatch({
type: GET_REQUEST,
payload: {name: 'Ioana new name'}
});
};
}
Reducer.js
import {
REQUEST_LOADING,
GET_REQUEST,
GET_REQUEST_ERROR
} from "./ExAction";
const initialState = {
success: false,
loading: false,
payload: {
name: "Ioana"
},
error: undefined,
};
const ExReducer = function(state = initialState, action) {
switch (action.type) {
case REQUEST_LOADING:
return {
...state,
success: false,
loading: true,
};
case GET_REQUEST:
return {
...state,
success: true,
loading: false,
type: action.type,
payload: action.payload,
error: undefined,
};
case GET_REQUEST_ERROR: {
return {
...state,
success: false,
loading: false,
type: action.type,
error: action.payload.response.data,
};
}
default: {
return state;
}
}
};
export default ExReducer;
RootReducer.js
import { combineReducers } from "redux";
import ExReducer from "./ExReducer";
const RootReducer = combineReducers({
reduxReducerData: ExReducer,
});
export default RootReducer;
Store.js
import { createStore, applyMiddleware } from "redux";
import RootReducer from "./RootReducer";
import thunk from 'redux-thunk';
const initialState = {};
export const reduxStore = createStore(
RootReducer,
initialState,
applyMiddleware(thunk)
);
App.js
import React from 'react';
import './App.css';
import { Provider } from "react-redux";
import { reduxStore } from "./redux/Store";
import CssBaseline from '@material-ui/core/CssBaseline';
import Container from '@material-ui/core/Container';
import FormPage from "./FormPage"
function App() {
return (
<Provider store={reduxStore}>
<CssBaseline />
<Container maxWidth="lg">
<FormPage />
</Container>
</Provider>
);
}
export default App;
FormPage.js
import React, { Component } from "react";
import { connect } from "react-redux";
import {
Grid,
} from "@material-ui/core";
import { getRequest, postRequest } from "./redux/ExAction"
import BasicForm from "./BasicForm"
class FormPage extends Component {
constructor(props){
super(props);
this.state = {
localReduxData: props.reduxData
}
}
componentDidMount() {
this.props.getRequest(1);
}
componentDidUpdate(prevProps, prevState, snapshot) {
if(this.props.reduxData !== prevProps.reduxData){
this.setState({ localReduxData: this.props.reduxData });
}
}
onHandleSubmit = (event) => {
console.log('Submitting ...')
this.props.postRequest(this.state.localReduxData);
}
onHandleChange = (event) => {
let {name, value} = event.target;
this.setState(prevState => ({...prevState,
localReduxData: {...prevState.localReduxData,
payload: {...prevState.payload, [name]: value}}
}), () => console.log('State updated ...'))
}
render() {
let { localReduxData } = this.state;
return(
<Grid container spacing={3}>
<Grid item xs={12}>
Forms w. Redux. Example
</Grid>
<Grid item xs={12}>
<BasicForm
data = {localReduxData.payload}
handleSubmit = {this.onHandleSubmit}
handleChange = {this.onHandleChange} />
</Grid>
</Grid>
)
}
}
const mapStateToProps = state => ({
reduxData: state.reduxReducerData
});
export default
connect(mapStateToProps, { getRequest, postRequest })(FormPage);
BasicForm.js
import React, { useEffect } from "react";
import { ValidatorForm, TextValidator } from "react-material-ui-form-validator";
import {
Button,
Grid,
} from "@material-ui/core";
export default function BasicForm(props) {
let { data } = props;
console.log(data)
useEffect(() => {
console.log('Use effect ...')
}, []);
const handleSubmit = event => {
props.handleSubmit(event);
};
const handleChange = event => {
props.handleChange(event)
}
return(
<ValidatorForm
onSubmit={handleSubmit}
onError={errors => null}
>
<Grid container spacing={1}>
<Grid item lg={6}>
<TextValidator
className="w-full"
label="Nume"
variant="outlined"
onChange={handleChange}
type="text"
name="name"
value={data.name}
validators={[
"required",
"minStringLength: 3",
"maxStringLength: 254"
]}
errorMessages={["Numele este necesar"]}
/>
</Grid>
<Grid item md={12}>
<Button color="primary" variant="outlined" type="submit">
<span className="pl-2 capitalize">Salveaza</span>
</Button>
</Grid>
</Grid>
</ValidatorForm>
)
}