“我的产品”组件通过映射的道具和调度连接到状态。从Products.js调用fetchProducts()应该调度“ FETCH_PRODUCTS”操作(和console.log(“由产品操作触发”)并将数据作为有效载荷发送到Product Reducer),这反过来又会增加从中获取的数据(产品)声明“ data.json”(也应该声明为console.log(“从products reducer发射”)),这又被假定为通过props.products或products将其提供给Products组件。来自Products.js的Console.logging(fetchProducts())记录其功能定义,该功能定义位于productActions.js中,我相信这是问题所在。
console.log(fetchProducts())
dispatch => {
console.log('Fired from product actions');
dispatch({
type: _components_types__WEBPACK_IMPORTED_MODULE_0__["FETCH_PRODUCTS"],
payload: data
});
}
Products.js
import React, { useState, useEffect } from "react";
import { Fade, Zoom } from "react-reveal";
import Modal from "react-modal";
import { connect } from "react-redux";
import fetchProducts from "../actions/productActions";
const Products = ({ props, products, add }) => {
const [product, setProduct] = useState(null);
const openModal = (product) => {
setProduct(product);
};
const closeModal = () => {
setProduct(null);
};
fetchProducts()
useEffect(() => {
}, []);
const renderProducts = () => {
return products.map((product) => {
return (
<li key={product._id}>
<a href={"#" + product._id} onClick={() => openModal(product)}>
<img src={product.image} />
</a>
<p>
<a href="#">{product.title}</a>
</p>
<p>
<strong>${product.price}.00</strong>
</p>
<button onClick={() => add(product)}>ADD TO CART</button>
</li>
);
});
};
return (
<div>
<Fade bottom cascade>
<ul>{!products ? <div>Loading...</div> : renderProducts()}</ul>
</Fade>
{product && (
<Modal isOpen={true}>
<Zoom clear cascade>
<div>
<p onClick={() => closeModal()}>X CLOSE</p>
<img src={product.image} />
</div>
<div>
<p>{product.title}</p>
<p>${product.price}.00</p>
Sizes
<p>
Available Sizes
{product.availableSizes.map((size) => {
return (
<>
<br />
<span> {size} </span>
</>
);
})}
</p>
<button
onClick={() => {
add(product);
closeModal();
}}
>
ADD TO CART
</button>
</div>
</Zoom>
</Modal>
)}
</div>
);
};
const mapStateToProps = state => {
return {
products: state.products.items
}
}
const mapDispatchToProps = () => {
return{
fetchProducts
}
}
export default connect(mapStateToProps, mapDispatchToProps())(Products);
productActions.js
import { FETCH_PRODUCTS } from "../components/types";
const data = require('../data.json')
const fetchProducts = () => (dispatch) => {
console.log('Fired from product actions')
dispatch({
type: FETCH_PRODUCTS,
payload: data
})
}
export default fetchProducts
productReducer.js
const { FETCH_PRODUCTS } = require("../components/types");
const productsReducer = (state = {}, action) =>{
switch(action.type){
case FETCH_PRODUCTS:
console.log('Fired from product reducers')
return {items: action.payload}
default:
return state;
}
}
export default productsReducer;
store.js
import {createStore, applyMiddleware, compose, combineReducers } from "redux";
import thunk from "redux-thunk";
import productsReducer from "./reducers/productReducers";
const initialState = {};
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(combineReducers({
products: productsReducer,
}),
initialState,
composeEnhancer(applyMiddleware(thunk))
)
export default store;
data.json
{
"products": [
{
"_id": "dress1",
"image": "/images/dress1.jpeg",
"title": "Dress 1",
"description": "about dress 1",
"availableSizes": ["S","M","L","XL"],
"price": 30.0
},
{
"_id": "dress2",
"image": "/images/dress2.jpeg",
"title": "Dress 2",
"description": "about dress 2",
"availableSizes": ["S","L", "XL"],
"price": 50.0
},
{
"_id": "dress3",
"image": "/images/dress3.jpeg",
"title": "Dress 3",
"description": "about dress 3",
"availableSizes": ["S", "L", "XL", "M"],
"price": 10.0
},
{
"_id": "dress4",
"image": "/images/dress4.jpeg",
"title": "Dress 4",
"description": "about dress 4",
"availableSizes": ["S","M","L", "XL", "XXL"],
"price": 150.0
},
{
"_id": "dress5",
"image": "/images/dress5.jpeg",
"title": "Dress 5",
"description": "about dress 5",
"availableSizes": ["M","L", "XXL"],
"price": 75.0
},
{
"_id": "dress6",
"image": "/images/dress6.jpeg",
"title": "Dress 6",
"description": "about dress 6",
"availableSizes": ["S","XXL"],
"price": 75.0
}
]
}
App.js
import React, { useEffect, useState } from "react";
import data from "./data.json";
import Products from "./components/Products";
import Filter from "./components/Filter";
import Cart from "./components/Cart";
import store from "./store";
import { Provider } from "react-redux";
const App = () => {
const [products, setProducts] = useState(data.products);
const [size, setSize] = useState("");
const [sort, setSort] = useState("");
const [cartItems, setCartItems] = useState([]);
useEffect(() => {}, [products]);
// console.log(fetch('data/data.json').then(response => response.text())
// .then(data => console.log(data)))
const addtoCart = (product) => {
const items = cartItems.slice();
let alreadyInCart = false;
items.forEach((element) => {
if (element._id === product._id) {
element.count++;
alreadyInCart = true;
}
});
if (!alreadyInCart) {
items.push({ ...product, count: 1 });
}
setCartItems(items);
// localStorage.setItem("cartItems", JSON.stringify(items))
};
const removeFromCart = (item) => {
const items = cartItems.slice();
setCartItems(items.filter((elem) => elem._id !== item._id));
};
const sortProducts = (e) => {
const sort = e.target.value;
setSort(sort);
setProducts(
products
.slice()
.sort((a, b) =>
sort === "lowest"
? a.price < b.price
? 1
: -1
: sort === "highest"
? a.price > b.price
? 1
: -1
: a._id < b._id
? 1
: -1
)
);
};
const createOrder = (order) => {
alert('Click "OK" to confirm order!', order);
};
const filterProducts = (e) => {
if (e.target.value === "") {
setProducts(data.products);
setSize(e.target.value);
} else {
setSize(e.target.value);
setProducts(
data.products.filter(
(product) => product.availableSizes.indexOf(e.target.value) >= 0
)
);
}
};
return (
<>
<Provider store={store}>
<header>
<a href="/">React Cart</a>
</header>
<Filter
size={size}
filter={filterProducts}
sort={sortProducts}
count={data.products.length}
/>
<hr />
<main>
<Products add={addtoCart} />
<hr />
<div>
<Cart
createOrder={createOrder}
remove={removeFromCart}
cartItems={cartItems}
/>
</div>
</main>
<hr />
<footer>Footer</footer>
</Provider>
</>
);
};
export default App;