weikee94 / react-ts

react with typescript
0 stars 0 forks source link

Reducer Example #5

Open weikee94 opened 2 years ago

weikee94 commented 2 years ago

see more

import { Dispatch, useReducer } from 'react';

type PizzaData = {
  numberOfPeople: number;
  slicesPerPerson: number;
  slicesPerPie: number;
};

type PizzaState = PizzaData & { pizzasNeeded: number };

// by using pizza action it will help us prevent like naming error 
type PizzaAction = {
  type:
    | 'UPDATE_NUMBER_OF_PEOPLE'
    | 'UPDATE_SLICES_PER_PERSON'
    | 'UPDATE_SLICES_PER_PIE';
  payload: number;
};

const calculatePizzasNeeded = ({
  numberOfPeople,
  slicesPerPerson,
  slicesPerPie
}: PizzaData): number => {
  return Math.ceil((numberOfPeople * slicesPerPerson) / slicesPerPie);
};

const addPizzasNeededToPizzaData = (data: PizzaData): PizzaState => {
  return { ...data, pizzasNeeded: calculatePizzasNeeded(data) };
};

const initialState: PizzaState = {
  numberOfPeople: 8,
  slicesPerPerson: 2,
  slicesPerPie: 8,
  pizzasNeeded: 2
};

const reducer = (state: PizzaState, action: PizzaAction) => {
  if (action.type === 'UPDATE_NUMBER_OF_PEOPLE') {
    return addPizzasNeededToPizzaData({
      ...state,
      numberOfPeople: action.payload
    });
  }

  if (action.type === 'UPDATE_SLICES_PER_PERSON') {
    return addPizzasNeededToPizzaData({
      ...state,
      slicesPerPerson: action.payload
    });
  }

  if (action.type === 'UPDATE_SLICES_PER_PIE') {
    return addPizzasNeededToPizzaData({
      ...state,
      slicesPerPie: action.payload
    });
  }

  return state;
};

const Calculation = ({ count }: { count: any }) => {
  return (
    <section className="calculation">
      <p className="count">{count}</p>
      <p className="caption">Pizzas Needed</p>
    </section>
  );
};

const Calculator = ({
  dispatch,
  state
}: {
  state: PizzaState;
  dispatch: Dispatch<PizzaAction>;
}) => {
  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
      }}
    >
      <label htmlFor="number-of-people">Number of People</label>
      <input
        id="number-of-people"
        type="number"
        value={state.numberOfPeople}
        onChange={(event) =>
          dispatch({
            type: 'UPDATE_NUMBER_OF_PEOPLE',
            payload: +event.target.value
          })
        }
      />
      <label htmlFor="slices-per-person">Slices Per Person</label>
      <input
        id="slices-per-person"
        type="number"
        value={state.slicesPerPerson}
        onChange={(event) =>
          dispatch({
            type: 'UPDATE_SLICES_PER_PERSON',
            payload: +event.target.value
          })
        }
      />
      <label htmlFor="slices-per-Pie">Slices Per Pie</label>
      <input
        id="slices-per-Pie"
        type="number"
        value={state.slicesPerPie}
        onChange={(event) =>
          dispatch({
            type: 'UPDATE_SLICES_PER_PIE',
            payload: +event.target.value
          })
        }
      />
    </form>
  );
};

const Application = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <main className="calculator">
      <header>
        <h1>Pizza Calculator</h1>
      </header>
      <Calculation count={state.pizzasNeeded} />
      <Calculator state={state} dispatch={dispatch} />
    </main>
  );
};

export default Application;
weikee94 commented 2 years ago

counter example

import { useReducer } from 'react';

type BasicCounterAction = {
  type: 'INCREMENT' | 'DECREMENT';
};

type SetCounterAction = {
  type: 'SET';
  payload: number;
}; 

// simple technique allow you to chose two different action
type BetterAction = BasicCounterAction | SetCounterAction;

type CounterAction = {
  type: 'INCREMENT' | 'DECREMENT' | 'SET';
  payload?: number;
};

type CounterState = {
  value: number;
};

const reducer = (state: CounterState, action: BetterAction) => {
  switch (action.type) {
    case 'INCREMENT':
      return { value: state.value + 1 };
    case 'DECREMENT':
      return { value: state.value - 1 };
    case 'SET':
      return { value: action.payload };
  }
};

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, { value: 0 });

  const increment = () => dispatch({ type: 'INCREMENT' });
  const decrement = () => dispatch({ type: 'DECREMENT' });
  const reset = () => dispatch({ type: 'SET', payload: 0 });

  return (
    <main className="Counter">
      <h1>Days Since Last Incident</h1>
      <p className="count">{state.value}</p>
      <section className="controls">
        <button onClick={increment}>Increment</button>
        <button onClick={reset}>Reset</button>
        <button onClick={decrement}>Decrement</button>
      </section>
    </main>
  );
};

const Application = () => <Counter />;

export default Application;