Repeat after me, “I don’t need Redux”. Seriously, you probably don’t. We didn’t need Redux in our SPA until we started adding more complicated forms with complicated validation. You can easily get away writing a React SPA without Redux. The state can be managed at a page level (or higher if you choose). It’s easy to create a React app without Redux. However, we decided that we needed Redux when complex validation appeared. We thought it would be better to extract that logic from the pages, into things called Actions and Reducers.
When we first started with Redux, we had no idea what we were doing. We’d been programming with React for about 7–8 months. This was a whole new thinking. This was a whole new ball game. How do you go from React to Redux? Well, for us, you do it component by component. We still have a lot of pages that aren’t in Redux because we haven’t needed to touch those pages. Why change something that works perfectly fine? Obviously, if we do need to reuse the other pages, we’ll make them Redux as well. It’s become routine for us.
How we’ve done Redux, is that we’ve made our pages stateless. Easy enough to do as most of our components are stateless. We then wrapped these pages with what we call a Container. This Container has the connection to our Reducers and Actions. It’s meant we’ve been able to keep the components visual only, without the complexities of state. How does a Container look like?
Let’s say you have a React component like below.
import * as React from 'react';import { Button } from 'react-bootstrap';
interface MyProps {onButtonClick: () => void;name: string;}
export const MyComponent: React.StatelessComponent<MyProps> = (props) => {return (<div><span>{props.name}</span><Button onClick={props.onButtonClick} /></div>);};
It has to get the state from somewhere, right? That’s where the container comes in.
import * as React from 'react';import { MyComponent } from './MyComponent';import { State } from './reducer';import { Dispatch, connect } from 'react-redux';import { onButtonClick } from './actions';
type StateToProps = {name: string;};
const mapStateToProps = (state: State): StateToProps => {return {name: state.name,};};
type DispatchToProps = {onButtonClick(): void;};
const mapDispatchToProps = (dispatch: Dispatch<State>): DispatchToProps => ({onButtonClick() {dispatch(onButtonClick())},});
export type MyComponentContainerProps = StateToProps & DispatchToProps;
export class MyComponentContainer extends React.Component<MyComponentContainerProps, {}>{
render() {return (<MyComponentname={this.props.name}onButtonClick={this.props.onButtonClick}/>);}};
export default connect(mapStateToProps, mapDispatchToProps)(MyComponentContainerContainer);
The container connects to your reducer, which is your keeper of state. Anything that changes your state has to come from your reducer. How does the reducer get the changes?
import { handleActions, Action } from 'redux-actions';
export const CHANGE_NAME = 'MYCOMPONENT.CHANGE_NAME';
export interface State {name: string;}
export const initialState: State = {name: 'John',}
export const myComponentReducer = handleActions<State, string>({[CHANGE_NAME]: (state, action: Action<string>) => {return {...state,name: action.payload,};},}, initialState);
Well, that’s where actions come in. Actions send a message to your reducer to say “Hey, I’ve got this event. It has this change here. Do what you want.” We can see that the event is the same string as in the reducer above, so it’ll trigger the state change on the reducer when the action is called.
import { CHANGE_NAME } from './State';import { Dispatch } from 'react-redux';import { createAction } from 'redux-actions';
const onButtonClickAction = createAction(CHANGE_NAME, (name: string) => name);
export const onButtonClick = () => {return (dispatch: Dispatch<string>) => {dispatch(onButtonClickAction('new name'));};};
There are other things you need to do, like add a provider and create a store etc. But you can look those up on Redux.
You can see by the example above, that this does add a lot of boilerplate code to your solution. If you think you don’t need Redux, then you probably don’t. In my next article, I’ll talk about React-Redux-Forms. Validation in React is quite a bit of work, but React-Redux-Forms certainly helped us minimise the noise in our components so that we have beautiful, clean code that anyone would be able to read and get the gist of pretty quickly.
Let me know how you guys use Redux. I’ll be keen to hear and see if we can improve.
Originally published at www.alexaitken.nz on February 19, 2018.