react/redoxプロジェクトの作成

60204 ワード

Redux installation ans setup


プロジェクトを作成し、react-router-domaxiosreact react-routerのパッケージをインストールします.

React/Redux Project Structure


プロジェクト内にディレクトリを作成して、構造を調整します.
src内部にフォルダとファイルを作成します.
containers:構成部品を含むフォルダ
redux:redux関連フォルダとファイル.
-actions:宣言されたActionsファイルに入ります.
-constants:action-typeファイルが入ります.
reducers:reduce関連ファイルが入ります.

Create Reduce Actions Types

constantsフォルダ内にaction-types.jsファイルが作成され、アクションタイプが作成されます.
export const ActionTypes = {
   SET_PRODUCTS : "SET_PRODUCTS",
   SELECTED_PRODUCT: "SELECTED_PRODUCT",
   REMOVESELECTED_PRODUCT: "REMOVE_SELECTED_PRODUCT",
}

Create Redux Actions

import { ActionTypes } from "../constants/action-types"
export const setProducts = (products) => {
    return {
        type:ActionTypes.SET_PRODUCTS,
        payload: products,

    }
};


export const selectedProduct = (product) => {
    return {
        type: ActionTypes.SELECTED_PRODUCT,
        payload: product,
    };
};

Create Reducers

productReducer.js
import { ActionTypes } from "../constants/action-types";

const initialState = {
    products: [{
        id : 1,
        title:"jyc",
        category:"human",
        },
    ],
}
export const productReducer = (state, {type, payload) => {
    switch(type){
        case ActionTypes.SET_PRODUCTS:
            return state;
        default:
            break;
    }
}
src/reducers/index.js
import { combineReducers } from "redux";
import { productReducer } from "./productReducer";

const reducers = combineReducers({
    allProducts: productReducer,
    
})

export default reducers

Create Redux Store

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

const store = createStore(
  reducers,
  {},
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSTION__()
);

export default store;

Connect React with Redux

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './redux/store';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
    <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

Create React Components

Header:ヘッダー
import React from 'react';

const Header = () => {
    return ( 
        <div className="ui fixed menu">
            <div className='ui container center'>
                <h2>WhateverShop</h2>
            </div>
        </div>
    )
}

export default Header
ProductComponents:製品
import React from 'react';

const ProductComponent = () => {
    return(
    <div>
        <h1>ProductComponent</h1>
    </div>
    )
}

export default ProductComponent
ProductDetail:製品リストをクリックしたときに表示される詳細
import React from 'react';

const ProductDetail = () => {
    return(
    <div>
        <h1>ProductDetail</h1>
    </div>
    )
}

export default ProductDetail
ProductListing:製品リスト
import React from 'react';

const ProductListing = () => {
    return(
    <div>
        <h1>ProductListing</h1>
    </div>
    )
}

export default ProductListing
まず簡単に作成し、ルートを追加して、それから詳しく作成します.

Add Routing to Projects

app.js
import "./App.css";
import Header from "./containers/Header";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import ProductListing from "./containers/ProductListing";
import ProductDetail from "./containers/ProductDetail";

function App() {
  return (
    <div className="App">
      <Router>
        <Header />
        <Routes>
          <Route path="/" exact component={ProductListing} />
          <Route path="/product/:productId" exact component={ProductDetail} />
          <Route>404 Not Found!</Route>
        </Routes>
      </Router>
    </div>
  );
}

export default App;

UseSelector to access state

ProductComponent.js
import React from 'react';
import { useSelector } from 'react-redux';

const ProductComponent = () => {
    const products = useSelector((state) => state.allProducts.products);
    const {id, title} = products[0];
    return(
    <div className="four column wide">
        <h1 className='ui link cards'>
            <div className='card'>
                <div className='image'></div>
                <div className='content'>
                    <div className='header'>{title}</div>
                </div>
            </div>
        </h1>
    </div>
    )
}

export default ProductComponent

Use Axios for Redux Api Call


faskstor apiを使用してアイテムの情報を取得します.

対応するコードを使用してデータをインポートします.ProductListing.js
import React ,{useEffect} from 'react';
import { useSelector } from 'react-redux';
import ProductComponent from './ProductComponent';
import axios from 'axios';
import { useDispatch } from 'react-redux';
import { setProducts } from '../redux/actions/prodectActions';

const ProductListing = () => {
    const products = useSelector((state) => state);
    const dispatch = useDispatch();

        const fetchProducts = async() => {
            const response = await axios.get('https://fakestoreapi.com/products').catch((err) => {
                console.log("ERR", err)
            });
            dispatch(setProducts(response.data));
        };

        useEffect(()=> {
            fetchProducts();
        },[]);
    console.log("products:" ,products);
    return(
    <div className='ui grid container'>
        <ProductComponent/>
    </div>
    )
}

export default ProductListing
再実行後、コンソールウィンドウを表示して、コンソール上のすべてのデータのステータスを表示できます.

Render Products Listing Page

import React from "react";
import { useSelector } from "react-redux";

const ProductComponent = () => {
  const products = useSelector((state) => state.allProducts.products);
  const renderList = products.map((product) => {
    const { id, title, image, price, category } = product;
    return (
      <div className="four wide column" key={id} >
        <div className="ui link cards">
          <div className="card">
            <div className="image">
              <img src={image} alt={title} />
            </div>
            <div className="content">
              <div className="header">{title}</div>
              <div className="meta price">$ {price}</div>
              <div className="meta">{category}</div>
            </div>
          </div>
        </div>
      </div>
    );
  });

  return (
      <>
        {renderList}
      </>
  )
};

export default ProductComponent;

クリックして詳細ページを作成します.
作成selectedProductReducer
export const selectedProductReducer = (state={},{type, payload}) => {
    switch(type) {
        case ActionTypes.SELECTED_PRODUCT:
            return {...state, ...payload};
        default:
            return state;
    }
}
組合せReducersに対応するReducerを追加します.
import { combineReducers } from "redux";
import { productReducer, selectedProductReducer } from "./productReducer";

const reducers = combineReducers({
    allProducts: productReducer,
    product:selectedProductReducer,
});


export default reducers;
詳細ページに記入します.このとき、fetchProductDetail関数が記述され、対応するproductIdデータのみが読み込まれてレンダリングされる.
import React, {useEffect} from 'react';
import {useParams} from 'react-router-dom'
import axios from 'axios';
import { useDispatch } from 'react-redux';
import { selectedProduct } from '../redux/actions/productActions';
import { useSelector } from 'react-redux';

const ProductDetail = () => {
    const product = useSelector((state) => state.product)
    const {image, title, price, category, description} = product
    const {productId} = useParams();
    const dispatch = useDispatch();
    console.log(product)

    const fetchProductDetail = async () => {
        const response = await axios.get(`https://fakestoreapi.com/products/${productId}`).catch(err => {
            console.log("Err", err);
        });

        dispatch(selectedProduct(response.data))
    }

    useEffect(() => {
        if(productId && productId !== "") fetchProductDetail();
    },[productId]);
    return (
        <div className="ui grid container">
          {Object.keys(product).length === 0 ? (
            <div>...Loading</div>
          ) : (
            <div className="ui placeholder segment">
              <div className="ui two column stackable center aligned grid">
                <div className="ui vertical divider">AND</div>
                <div className="middle aligned row">
                  <div className="column lp">
                    <img className="ui fluid image" src={image} />
                  </div>
                  <div className="column rp">
                    <h1>{title}</h1>
                    <h2>
                      <a className="ui teal tag label">${price}</a>
                    </h2>
                    <h3 className="ui brown block header">{category}</h3>
                    <p>{description}</p>
                    <div className="ui vertical animated button" tabIndex="0">
                      <div className="hidden content">
                        <i className="shop icon"></i>
                      </div>
                      <div className="visible content">Add to Cart</div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          )}
        </div>
      );
}

export default ProductDetail
現状

必要なアイテムをクリックすると、詳細ページに移動し、そのアイテムの詳細ページが表示されますが、少しおかしいです.
最初のアイテムをクリックし、別のアイテムをクリックすると、以前にクリックしたアイテムの情報が保存後に更新されるのをしばらく見ることができます.
したがって、「詳細項目」をクリックすると、残りの項目の情報が消去されます.

Select and Remove Action Types


だから私はこれをしました.
ユーザーは、プロジェクトを選択してからプロジェクトを選択し、ロードで以前に選択したすべてのプロジェクトのデータを削除し、以前の情報を保持しない役割を果たします.selectedProductReducer内部にREMOVE SELECTED PRODUCTをケースとして追加.productReducer.js
export const selectedProductReducer = (state={},{type, payload}) => {
    switch(type) {
        case ActionTypes.SELECTED_PRODUCT:
            return {...state, ...payload};
        case ActionTypes.REMOVE_SELECTED_PRODUCT:
            return {};
        default:
            return state;
    }
}
そして、動作生成関数removeSelectedProductが記述される.productActions.js
export const removeSelectedProduct = () => {
    return {
        type: ActionTypes.REMOVE_SELECTED_PRODUCT,
    };
};
最後に、ProductDetailuseEffectの内容を削除します.このコンテンツをレンダリングする前に、dispatchを使用してremoveSelectedProductを実行します.
 useEffect(() => {
        if(productId && productId !== "") fetchProductDetail();
        return () => {
            dispatch(removeSelectedProduct());
        }
    },[productId]);
結果

今は選択しても、以前の情報は残さず、欲しいデータだけが表示されます.