Thanks to visit codestin.com
Credit goes to github.com

Skip to content

joshborup/redux-1-mini

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

54 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
  • Open ./src/store.js
  • Import createStore from Redux.
  • Create an initial state with a currentValue property for our counter.
  • Write a simple reducer.
  • Create and export a Redux store.

Solution

./src/store.js
import { createStore } from "redux";

const initialState = { currentValue: 0 };

function counter( state = initialState, action ) {
	return state;
}

export default createStore(counter);

Step 2

Summary

In this step, we'll give our Counter component access to the store.

Instructions

  • Open ./src/Counter.js.
  • Import store from ./src/store.js.
  • Setup state for Counter.
    • State should have a property called store.
      • Use the getState method to copy the Redux state to the store property.
  • Destructure currentValue from state in your render method.
  • Update the h1 to use the currentValue.
  • Update the JSON.stringify method to display the store property on state.

Solution

./src/Counter.js
import React, { Component } from "react";
import store from "./src/store";

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      store: store.getState();
    }
  }
  render() {
    const {
      currentValue
    } = this.state.store;
    return (
      <div className="app">
        <section className="counter">
          <h1 className="counter__current-value">{currentValue}</h1>
          /* lots of jsx */
        </section>
        <section className="state">
          <pre>{JSON.stringify(this.state.store, null, 2)}</pre>
        </section>
      </div>
    );
  }
}

export default Counter;

Step 3

Summary

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

Instructions

  • Open ./src/store.js.
  • Create and exportINCREMENT and DECREMENT action types.
  • 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/store.js
import { createStore } from 'redux';

const initialState = { currentValue: 0 };

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

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 default createStore(counter);

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 types.
  • Create an increment and a decrement method.
    • The methods should accept an amount parameter.
    • The component method should use the Redux dispatch method to send an action to the reducer.
      • The action should include the action type you imported.
      • The action should include the amount.
  • Update the .counter_button buttons to call increment or decrement with the correct amount.
  • In componentDidMount, use the Redux subscribe method to update local state.
    • The subscribe method will use the getState method to update.

Solution

./src/Counter.js
import React, { Component } from "react";
import store, { INCREMENT, DECREMENT } from "./store.js";

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      store: store.getState()
    };
    this.increment = this.increment.bind(this);
    this.decrement = this.decrement.bind(this);
  }
  componentDidMount() {
    store.subscribe(() => {
      this.setState({
        store: store.getState()
      });
    });
  }
  increment(amount) {
    store.dispatch({ amount, type: INCREMENT });
  }
  decrement(amount) {
    store.dispatch({ amount, type: DECREMENT });
  }
  render() {
    const {
      currentValue
    } = this.state.store;
    return (
      <div className="app">
        <section className="counter">
          <h1 className="counter__current-value">{currentValue}</h1>
          <div className="counter__button-wrapper">
            <button className="counter__button" onClick={() => this.increment(1)}>
              +1
            </button>
            <button className="counter__button" onClick={() => this.increment(5)}>
              +5
            </button>
            <button className="counter__button" onClick={() => this.decrement(1)}>
              -1
            </button>
            <button className="counter__button" onClick={() => this.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.state.store, null, 2)}
          </pre>
        </section>
      </div>
    );
  }
}

export default Counter;

Step 5

Summary

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

Instructions

  • Open ./src/store.js.
  • Create and export UNDO and REDO action types.
  • Refactor initialState and counter to handle undo/redo logic.

Solution

./src/store.js
import { createStore } from 'redux';

const initialState = {
  currentValue: 0,
  futureValues: [],
  previousValues: []
};

export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const UNDO = 'UNDO';
export const REDO = 'REDO';

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 default createStore(counter);

Step 6

Summary

In this step, we'll import UNDO and REDO action types into our Counter.js and write methods to dispatch them.

Instructions

  • Open ./src/Counter.js.
  • Import UNDO and REDO action types.
  • Create an undo and a redo method.
    • The component method should use the Redux dispatch method to send an action to the reducer.
      • The action should include the action type you imported.
  • Hook up the undo and redo buttons to their respective methods.
  • Destructure previousValues and futureValues from the store in the render method.
  • Update the disabled attributes of the buttons to use previousValues and futureValues respectively.

Solution

./src/Counter.js
import React, { Component } from "react";
import store, { INCREMENT, DECREMENT, UNDO, REDO } from "./store.js";

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      store: store.getState()
    };
    this.increment = this.increment.bind(this);
    this.decrement = this.decrement.bind(this);
    this.undo = this.undo.bind(this);
    this.redo = this.redo.bind(this);
  }
  componentDidMount() {
    store.subscribe(() => {
      this.setState({
        store: store.getState()
      });
    });
  }

  increment(amount) {
    store.dispatch({ amount, type: INCREMENT });
  }
  decrement(amount) {
    store.dispatch({ amount, type: DECREMENT });
  }
  undo() {
    store.dispatch({ type: UNDO });
  }
  redo() {
    store.dispatch({ type: REDO });
  }
  render() {
    const {
      currentValue,
      futureValues,
      previousValues
    } = this.state.store;

    return (
      <div className="app">
        <section className="counter">
          <h1 className="counter__current-value">{currentValue}</h1>
          <div className="counter__button-wrapper">
            <button className="counter__button" onClick={() => this.increment(1)}>
              +1
            </button>
            <button className="counter__button" onClick={() => this.increment(5)}>
              +5
            </button>
            <button className="counter__button" onClick={() => this.decrement(1)}>
              -1
            </button>
            <button className="counter__button" onClick={() => this.decrement(5)}>
              -5
            </button>
            <br />
            <button
              className="counter__button"
              disabled={previousValues.length === 0}
              onClick={this.undo}
            >
              Undo
            </button>
            <button
              className="counter__button"
              disabled={futureValues.length === 0}
              onClick={this.redo}
            >
              Redo
            </button>
          </div>
        </section>
        <section className="state">
          <pre>{JSON.stringify(this.state.store, null, 2)}</pre>
        </section>
      </div>
    );
  }
}
export default 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

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 78.8%
  • CSS 16.2%
  • HTML 5.0%