Giter Site home page Giter Site logo

react-5-mini's Introduction

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.

react-5-mini's People

Contributors

brianjhudson avatar devlemire avatar jrobber avatar r-walsh avatar steven-isbell avatar tommydreamer57 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.