Skip to content

A mini project to introduce how to use Redux in a react application.

Notifications You must be signed in to change notification settings

DevMountain/react-5-mini

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Project Summary

In this project, we'll create a small counter application using React and Redux. We'll also include extra functionality for undo/redo actions.

Live Example

Click Me!

Setup

  • fork and clone this repository.
  • cd into the project root.
  • Run npm install to fetch the project dependencies.
  • Run npm start to spin up a development server.

Step 1

Summary

In this step, we'll install some new dependencies, create a reducer, and create a Redux store.

Instructions

  • Install redux and react-redux
  • Create an initial state src/ducks/counter.js
  • Write a simple reducer in src/ducks/counter.js
  • Create a Redux store in src/store.js

Solution

./src/ducks/counter.js
const initialState = { currentValue: 0 };

export default function counter( state = initialState, action ) {
	return state;
}
./src/store.js
import { createStore } from "redux";

import counter from "./ducks/counter";

export default createStore(counter);

Step 2

Summary

In this step, we'll make our application aware that redux exists and connect the Counter component.

Instructions

  • Open src/App.js.
  • Import Provider from react-redux.
  • Import store from ./src/store.js.
  • Wrap the App component in the Provider component.
    • Add a store prop that equals our imported store.
  • Open ./src/Counter.js.
  • Import connect from react-redux.
  • Connect the Counter component to Redux.
    • Use a mapStateToProps function that takes in state.
      • Return state for now.

Solution

./src/App.js
import React, { Component } from "react";
import { Provider } from "react-redux";

import store from "./store";
import "./App.css";

import Counter from "./Counter";

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <Counter />
      </Provider>
    );
  }
}

export default App;
./src/Counter.js
import React, { Component } from "react";
import { connect } from "react-redux";

class Counter extends Component {
  render() {
    return (
      /* lots of jsx */
    );
  }
}

const mapStateToProps = state => state;

export default connect(mapStateToProps)(Counter);

Step 3

Summary

In this step, we'll set up Redux to actually execute actions. We'll start by creating action types, creating action creators, and implementing increment/decrement logic.

Instructions

  • Open ./src/ducks/counter.js.
  • Create INCREMENT and DECREMENT action types.
  • Write action creators corresponding to INCREMENT and DECREMENT action types.
    • Each of these action creators should accept an amount argument.
  • Update the reducer to process these actions into state changes.
    • INCREMENT should increment currentValue by the given amount.
    • DECREMENT should decrement currentValue by the given amount.

Solution

./src/ducks/counter.js
const initialState = { currentValue: 0 };

const INCREMENT = "INCREMENT";
const DECREMENT = "DECREMENT";

export default function counter(state = initialState, action) {
  switch (action.type) {
    case INCREMENT:
      return { currentValue: state.currentValue + action.amount };
    case DECREMENT:
      return { currentValue: state.currentValue - action.amount };
    default:
      return state;
  }
}

export function increment(amount) {
  return { amount, type: INCREMENT };
}

export function decrement(amount) {
  return { amount, type: DECREMENT };
}

Step 4

Summary

In this step, we'll wire up the Counter component so that it can dispatch actions to our reducer.

Instructions

  • Open ./src/Counter.js.
  • Import the increment and decrement action creators.
  • Use connect's mapDispatchToProps to place the action creators on Counter's props.
  • Update the .counter_button buttons to call increment or decrement with the correct amount.

Solution

./src/Counter.js
import React, { Component } from "react";
import { connect } from "react-redux";

import { decrement, increment } from "./ducks/counter";

class Counter extends Component {
  render() {
    const { currentValue, decrement, increment } = this.props;

    return (
      <div className="app">
        <section className="counter">
          <h1 className="counter__current-value">{currentValue}</h1>
          <div className="counter__button-wrapper">
            <button
              className="counter__button"
              onClick={() => increment(1)}
            >
              +1
            </button>
            <button
              className="counter__button"
              onClick={() => increment(5)}
            >
              +5
            </button>
            <button
              className="counter__button"
              onClick={() => decrement(1)}
            >
              -1
            </button>
            <button
              className="counter__button"
              onClick={() => decrement(5)}
            >
              -5
            </button>
            <br />
            <button
              className="counter__button"
              disabled={true}
              onClick={() => null}
            >
              Undo
            </button>
            <button
              className="counter__button"
              disabled={true}
              onClick={() => null}
            >
              Redo
            </button>
          </div>
        </section>
        <section className="state">
          <pre>
            {JSON.stringify(this.props, null, 2)}
          </pre>
        </section>
      </div>
    );
  }
}

const mapStateToProps = state => state;

export default connect(mapStateToProps, { decrement, increment })(Counter);

Step 5

Summary

In this step, we'll implement undo/redo logic into our reducer.

Instructions

  • Open ./src/ducks/counter.js.
  • Create UNDO and REDO action types.
  • Write action creators for UNDO and REDO.
  • Refactor initialState and counter to handle undo/redo logic.

Solution

./src/ducks/counter.js
const initialState = {
  currentValue: 0,
  futureValues: [],
  previousValues: []
};

const INCREMENT = "INCREMENT";
const DECREMENT = "DECREMENT";
const UNDO = "UNDO";
const REDO = "REDO";

export default function counter(state = initialState, action) {
  switch (action.type) {
    case INCREMENT:
      return {
        currentValue: state.currentValue + action.amount,
        futureValues: [],
        previousValues: [state.currentValue, ...state.previousValues]
      };
    case DECREMENT:
      return {
        currentValue: state.currentValue - action.amount,
        futureValues: [],
        previousValues: [state.currentValue, ...state.previousValues]
      };
    case UNDO:
      return {
        currentValue: state.previousValues[0],
        futureValues: [state.currentValue, ...state.futureValues],
        previousValues: state.previousValues.slice(1)
      };
    case REDO:
      return {
        currentValue: state.futureValues[0],
        futureValues: state.futureValues.slice(1),
        previousValues: [state.currentValue, ...state.previousValues]
      };
    default:
      return state;
  }
}

export function increment( amount ) {
  return { amount, type: INCREMENT };
}

export function decrement( amount ) {
  return { amount, type: DECREMENT };
}

export function undo() {
  return { type: UNDO };
}

export function redo() {
  return { type: REDO };
}

Step 6

Summary

In this step, we'll import undo and redo action creators into our Counter.js and hook them up their respective buttons.

Instructions

  • Open ./src/Counter.js.
  • Import undo and redo action creators.
  • Add undo and redo to mapDispatchToProps.
  • Destrucuture undo and redo from props.
  • Hook up the undo and redo buttons to their respective action creators.

Solution

./src/Counter.js
import React, { Component } from "react";
import { connect } from "react-redux";

import { decrement, increment, redo, undo } from "./ducks/counter";

class Counter extends Component {
  render() {
    const {
      currentValue,
      decrement,
      futureValues,
      increment,
      previousValues,
      redo,
      undo
    } = this.props;
    return (
      <div className="app">
        <section className="counter">
          <h1 className="counter__current-value">{currentValue}</h1>
          <div className="counter__button-wrapper">
            <button
              className="counter__button"
              onClick={() => increment(1)}
            >
              +1
            </button>
            <button
              className="counter__button"
              onClick={() => increment(5)}
            >
              +5
            </button>
            <button
              className="counter__button"
              onClick={() => decrement(1)}
            >
              -1
            </button>
            <button
              className="counter__button"
              onClick={() => decrement(5)}
            >
              -5
            </button>
            <br />
            <button
              className="counter__button"
              disabled={previousValues.length === 0}
              onClick={undo}
            >
              Undo
            </button>
            <button
              className="counter__button"
              disabled={futureValues.length === 0}
              onClick={redo}
            >
              Redo
            </button>
          </div>
        </section>
        <section className="state">
          <pre>
            {JSON.stringify(this.props, null, 2)}
          </pre>
        </section>
      </div>
    );
  }
}

const mapStateToProps = state => state;

export default connect(mapStateToProps, { decrement, increment, redo, undo })(Counter);

Contributions

If you see a problem or a typo, please fork, make the necessary changes, and create a pull request so we can review your changes and merge them into the master repo and branch.

Copyright

© DevMountain LLC, 2017. Unauthorized use and/or duplication of this material without express and written permission from DevMountain, LLC is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to DevMountain with appropriate and specific direction to the original content.

About

A mini project to introduce how to use Redux in a react application.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published