tester
tester

Reputation: 23199

Best way to setup a React useReducer

I setup this reducer.js file to use React's useReducer

https://reactjs.org/docs/hooks-reference.html#usereducer

import {useReducer} from 'react';

const initialState = {
  test: 0,
};

const reducer = (state, action) => {
  switch (action.type) {
    case 'reset':
      return initialState;
    case 'addTest':
      return {test: state.test + 1};
    case 'removeTest':
      return {test: state.test - 1};
  }
};

export const getReducer = () => {
  return useReducer(reducer, initialState);
};

Now I can get state and dispatch via getReducer in different rendering functions:

import React from 'react';
import ReactDOM from 'react-dom';

import {getReducer} from './reducer';

const Button = (props) => (
  <button
    type="button"
    onClick={() => props.dispatch({type: props.type})}>
    {props.children}
  </button>
);

const App = () => {
  const [state, dispatch] = getReducer();
  return (
    <React.Fragment>
      {state.test}
      <Button dispatch={dispatch} type="addTest">Add 1</Button>
      <Button dispatch={dispatch} type="removeTest">Remove 1</Button>
      <Button dispatch={dispatch} type="reset">Reset</Button>
    </React.Fragment>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));

It does feel weird passing the dispatch function around and having other components call props.dispatch. Is there a cleaner way to set this up?

I setup a repo here if you want to try out different patterns: https://github.com/dancrew32/hooks

Upvotes: 0

Views: 4832

Answers (3)

Ininity
Ininity

Reputation: 16

This is how to set a context reducer.

import { useState, useEffect } from 'react';
import axios from 'axios';
import { useContext, createContext } from 'react';
import { AuthReducer } from './Reducer';
import { initialstate } from './Reducer';
import { useReducer } from 'react';

export const ProductStateContext = createContext();

export const useProductState = () => {
  return useContext(ProductStateContext);
};

export const ProductProvider = ({ children }) => {
  const [items, setItems] = useState([]);
  const [product, setProduct] = useState('');
  const [detail, setDetail] = useState([]);
  const [cart, dispatch] = useReducer(AuthReducer, initialstate);
  const [qty, setQty] = useState(0);
  const [dataCarts, setDataCarts] = useState();
  const [isSearch, setIsSearch] = useState(false)

  useEffect(() => {
    axios.get('https://fakestoreapi.com/products').then(
      response => {
        setItems(response.data);
      },
      error => {
        console.log(error);
      }
    );
  }, []);


  const searchUser = search => {
    console.log(search, "ini value")
    setProduct(
      items.filter(item => {
        return search.toLowerCase() === ''
          ? item
          : item.title.toLowerCase().includes(search) ||
            search.toLowerCase() === ''
            ? item
            : item.category.toLowerCase().includes(search);
      })
    );
  };

  // Detail Produk
  const detailProduct = item => {
    setDetail(item);
  };

  // QUANTITY
  let quantity = 0;

  useEffect(() => {
    if (cart.cart.length !== 0) {
      cart.cart.map(item => {
        quantity += item.quantity;
        setQty(quantity);
      });
    } else {
      setQty(0);
    }
  });

  // TOTAL HARGA
  const hitungTotalHarga = () => {
    const totalHarga = cart.cart.reduce(
      (acc, item) => acc + item.products.price * item.quantity,
      0
    );
    return totalHarga.toFixed(2);
  };

  const cartValue = (item, index) => {
    setDataCarts(item, index);
  };

  return (
    <ProductStateContext.Provider
      value={{
        product,
        searchUser,
        items,
        detailProduct,
        detail,
        cart,
        dispatch,
        qty,
        hitungTotalHarga,
        cartValue,
        dataCarts,
        setIsSearch,
        isSearch
      }}
    >
      {children}
    </ProductStateContext.Provider>
  );
};

Upvotes: -1

Ininity
Ininity

Reputation: 16

import Swal from 'sweetalert2';
const userLists = JSON.parse(localStorage.getItem('user_data'));

export const loginUser = (dispatch, email, password) => {
  const filtered = userLists.findIndex(obj => {
    return obj.email === email;
  });

  if (
    (email === userLists[filtered].email || email === "[email protected]") &&
    (password === userLists[filtered].sandi || password === 12345)
  ) {
    dispatch({
      type: 'SUCCESS_LOGIN',
      islogin: true,
    });
    Swal.fire({
      icon: 'success',
      title: 'Selamat',
      text: 'Anda telah masuk',
      footer: '<a href="">Selamat berbelanja</a>',
    });
    localStorage.setItem('nama', JSON.stringify(userLists[filtered].namaDepan));
    localStorage.setItem('dataUser', JSON.stringify(userLists[filtered].email));
    localStorage.setItem('islogin', JSON.stringify(true));

    // console.log('ini user');
  } else {
    dispatch({
      type: 'FAILED_LOGIN',
    });
    Swal.fire({
      icon: 'error',
      title: 'Oops...',
      text: 'Something went wrong!',
      footer: '<a href="">Why do I have this issue?</a>',
    });
    localStorage.setItem('islogin', JSON.stringify(false));
  }
};

export const logOutUser = (dispatch, user) => {
  console.log(user, 'ini user logout');
  dispatch({
    type: 'SUCCESS_LOGOUT',
  });
  localStorage.setItem('islogin', JSON.stringify(false));

  localStorage.removeItem('nama');
};

export const addCart = (dispatch, payloadProduct) => {
  console.log(dispatch, 'ini dispatch');
  dispatch({
    type: 'ADD_CART',
    cart: payloadProduct,
    quantity: 0,
  });
};
export const addWish = (dispatch, payloadProduct) => {
  console.log(dispatch, 'ini dispatch');
  dispatch({
    type: 'ADD_WISH',
    cart: payloadProduct,
  });
};

let islogin = localStorage.getItem('islogin')
  ? JSON.parse(localStorage.getItem('islogin'))
  : false;
export const initialstate = {
  islogin: false,
  cart: [],
  wish: [],
  quantity: 0,
  love: false,
};

export const AuthReducer = (initialstate, action) => {
  switch (action.type) {
    case 'SUCCESS_LOGIN':
      return {
        ...initialstate,
        user: action.email,
        islogin: true,
      };
    case 'SUCCESS_LOGOUT':
      return {
        ...initialstate,
        islogin: false,
      };
    case 'ADD_CART':
      return {
        ...initialstate,
        cart: action.cart,
        quantity: action.quantity++,
      };
    case 'ADD_WISH':
      return {
        ...initialstate,
        wish: action.cart,
      };

    default:
      return { ...initialstate };
  }
};

Upvotes: -1

blaz
blaz

Reputation: 4078

How about defining your actions and mapping them to your reducer?

const mapDispatch => dispatch => ({
  reset: () => dispatch({ type: 'reset' }),
  addTest: () => dispatch({ type: 'addTest' }),
  removeTest: () => dispatch({ type: 'removeTest' })
})

const Button = (props) => (
  <button
    type="button"
    onClick={props.onClick}>
    {props.children}
  </button>
);

const App = () => {
  const [state, dispatch] = getReducer();
  const actions = mapDispatch(dispatch)
  return (
    <React.Fragment>
      {state.test}
      <Button onClick={actions.addTest}>Add 1</Button>
      <Button onClick={actions.removeTest}>Remove 1</Button>
      <Button onClick={actions.reset}>Reset</Button>
    </React.Fragment>
  );
};

Nothing new here; just a copycat of how react-redux does things.

Upvotes: 6

Related Questions