Я пытаюсь создать список продуктов в React, где я могу добавлять и удалять продукты.
Я начал исследовать, как я могу сделать это, используя инфраструктуру/платформу редукс, и реагировать на нативные
У меня уже есть функциональный контейнер productList, компонент product и компонент cartList, cartProduct.
Мои проблемы:
Продукты: я могу только добавлять продукты, но не удалять
Корзина: Наоборот + в корзине не обновляется статус товаров в корзине.
Я добавил bindActionCreator, но пока не знаю, как применить его к моему productList.
Чего я ожидаю? Я пытаюсь добавлять и удалять продукты из магазина реагирования в том же контейнере/компоненте.
Как я могу это сделать? Правилен ли мой подход или я совершенно не прав?
Заранее спасибо.
ProductActionCreators
export const ADD_TO_CART = 'ADD_TO_CART'
export const REMOVE_FROM_CART = 'REMOVE_FROM_CART'
export function addItemToCart(row) {
return {
type:'ADD_TO_CART',
payload: row, qty
}
}
export function removeTodo(row) {
return {
type:'REMOVE_FROM_CART' ,
payload: row, qty
}
}
Список продуктов (упрощенный)
import React from 'react';
import { Component } from 'react';
import {
View,
StyleSheet,
Text
} from 'react-native';
import Products from '../components/Products';
import { bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import * as ProductActionCreators from '../actions/ProductActionCreators'
export class ProductList extends React.Component {
static navigationOptions = {
header: null,
};
constructor(props) {
super(props);
const { rows } = this.props.navigation.state.params;
const arrays = Object.values( {rows});
this.state = {
arrays,
filteredProducts: arrays,
};
const { dispatch } = props
this.boundActionCreators = bindActionCreators(ProductActionCreators, dispatch)
console.info(this.boundActionCreators)
}
render() {
return (
<View style = {styles.container} >
<Text style = {styles.title} >
{this.state.arrays[0].name}
</Text>
<Products products = {this.state.arrays[0].data} onPress=
//Trying to change this to multiple actions
{this.props.addItemToCart}/>
</View>
)
}
}
const qty = 0;
const mapDispatchToProps = (dispatch) =>{
//need to add BindActionCreator
return{
addItemToCart:(row) => dispatch({
type:'ADD_TO_CART', payload: row, qty
}),
removeItem:(product) => dispatch ({
type:'REMOVE_FROM_CART' , payload: product, qty
})
}
}
export default connect(null, mapDispatchToProps) (ProductList);
Продукт (упрощенный)
import React, { Component } from "react";
import {
View,
Text,
TouchableOpacity,
TextInput,
FlatList,
} from "react-native";
import Icon from "react-native-vector-icons/Ionicons";
class Products extends Component {
constructor(props) {
super(props);
const { products } = this.props;
this.state = {
products,
filteredProducts: products,
};
}
renderProducts = (products) => {
return (
<View key = {products.index}>
<View>
<Icon name = {products.item.icon} color = "#DD016B" size = {25} />
</View>
<View>
<Text style = {styles.name}>
{products.item.name}
</Text>
<Text>
€ {products.item.price}
</Text>
</View>
<View style = {styles.buttonContainer}>
<TouchableOpacity onPress = {() => this.props.onPress(products.item)} >
<Icon name = "ios-add" color = "white" size = {25} />
</TouchableOpacity>
<TouchableOpacity onPress = {() => this.props.onPress(products.item)} >
<Icon name = "ios-remove" color = "white" size = {25} />
</TouchableOpacity>
</View>
</View>
)
}
render() {
return (
<View>
<FlatList
style = {styles.listContainer}
data = {this.state.filteredProducts}
renderItem = {this.renderProducts}
keyExtractor = {(item, index) => index.toString()}
/>
</View>
);
}
}
export default Products;
редукторы/корзина
const cartItems = (state = [], action) => {
switch (action.type)
{
case 'ADD_TO_CART':
if (state.some(cartItem => cartItem.id === action.payload.id)) {
// increase qty if item already exists in cart
return state.map(cartItem => (
cartItem.id === action.payload.id ? { ...cartItem, qty: cartItem.qty + 1 } : cartItem
));
}
return [...state, { ...action.payload, qty: 1 }];
// else add the new item to cart
case 'REMOVE_FROM_CART':
return state
.map(cartItem => (cartItem.id === action.payload.id ? { ...cartItem, qty: cartItem.qty - 1 } : cartItem))
.filter(cartItem => cartItem.qty > 0);
}
return state
}
export default cartItems
магазин/индекс
import {createStore} from 'redux';
import cartItems from '../reducers/carItems';
export default store = createStore(cartItems)
Структура приложения (упрощенная)
Main folder
↳
Containers(folder)
↳
ProductsList.js
CartList.js
Components(folder)
↳
Product.js
cartProduct.js
Reducers(folder)
↳
carItems.js
Actions(folder)
↳
ProductActionCreators.js
Navigation(folder)
↳
AppNavigator,js
MainTabNavigator.js
Assets(folder for images etc.)
Store(folder)
↳
index.js
App.JS
Data.JS (using static JSON file for this development phase)





Код выглядит нормально по большей части.
Часть подключения реакции-редукции в ProductList выглядит отключенной. количество всегда равно 0. Должно быть 1.
Также должен присутствовать mapStateToProps для получения товаров из корзины.
Спасибо за Ваш ответ. Кол-во товара начинается с 0, потому что в данный момент в корзине нет товара. Я думал, но я проверю это сегодня вечером. Что касается mapStateToProps, не могли бы вы объяснить немного больше?
В вашем ProductList я бы подошёл к привязке действия так:
const mapDispatchToProps = (dispatch) =>{
return bindActionCreators({
addItemToCart: (row, qty) => dispatch({
type:'ADD_TO_CART', payload: {row, qty}
}),
removeItem: (product, qty) => dispatch({
type:'REMOVE_FROM_CART' , payload: {product, qty}
})
})
}
export default connect(null, mapDispatchToProps)(ProductList);
Удалите привязку действия из конструктора вашего компонента, так как это не нужно.
Вы можете разделить код на подход Container/Component/HOC, так как я считаю, что код намного легче читать. Поскольку вы определили свои действия в отдельном файле, я бы также импортировал их, а не объявлял заново.
Если вы последуете этому совету, вы получите следующее:
контейнер.js
import { bindActionCreators } from 'redux';
import ProductList from './product-list';
// Actions
import { addItemToCart, removeItem } from './actions';
function mapStateToProps(state) {
return {}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
addItemToCart,
removeItem,
})
}
export default connect(mapStateToProps, mapDispatchToProps)(ProductList);
список продуктов.js
import React from 'react';
import { View, Text } from 'react-native';
import Products from '../components/Products';
export class ProductList extends React.Component {
static navigationOptions = {
header: null,
};
constructor(props) {
super(props);
const { rows } = this.props.navigation.state.params;
const arrays = Object.values( {rows});
this.state = {
arrays,
filteredProducts: arrays,
};
this.handleProductPress = this.handleProductPress.bind(this);
}
handleProductPress(e) {
e.preventDefault();
// This is pseudo code...
this.props.addItemToCart(e.target.value, 1);
return;
}
render() {
return (
<View style = {styles.container} >
<Text style = {styles.title} >
{this.state.arrays[0].name}
</Text>
<Products products = {this.state.arrays[0].data} onPress = {this.handleProductPress} />
</View>
)
}
}
export default ProductList;
Поиграйте с этим и посмотрите, как у вас получится.
Спасибо за Ваш ответ. Вечером посмотрю более внимательно :)
У вас есть два разных действия, addItemToCart, removeItem, которые вы определяете в mapDispatchToProps. Теперь, когда вы указываете аргумент mapDispatchToProps для подключения, метод отправки недоступен в качестве реквизита для подключенного компонента, вместо этого доступны только методы, возвращаемые mapDispatchToProps.
Второй, вам не нужно использовать bindActionCreators и уж точно не в компоненте. MapDispatchToProps может быть просто объектом, и соединение будет использовать внутреннюю отправку.
Третий, вам нужно передать действия добавления и удаления как дочернему компоненту.
Четвертый вы можете передать несколько действий компоненту продукта просто как реквизит
Ваш код будет выглядеть так
ProductActionCreators.js
export const ADD_TO_CART = 'ADD_TO_CART'
export const REMOVE_FROM_CART = 'REMOVE_FROM_CART'
export function addItemToCart(row) {
return {
type:'ADD_TO_CART',
payload: row
}
}
export function removeItem(item) {
return {
type:'REMOVE_FROM_CART' ,
payload: item
}
}
Список продуктов
import React from 'react';
import { Component } from 'react';
import {
View,
StyleSheet,
Text
} from 'react-native';
import Products from '../components/Products';
import { connect } from 'react-redux';
import { addItemToCart, removeItem } from '../actions/ProductActionCreators';
export class ProductList extends React.Component {
static navigationOptions = {
header: null,
};
constructor(props) {
super(props);
const { rows } = this.props.navigation.state.params;
const arrays = Object.values( {rows});
this.state = {
arrays,
filteredProducts: arrays,
};
}
render() {
return (
<View style = {styles.container} >
<Text style = {styles.title} >
{this.state.arrays[0].name}
</Text>
<Products products = {this.state.arrays[0].data} addItemToCart = {this.props.addItemToCart} removeItem = {this.props.removeItem}/>
</View>
)
}
}
const mapDispatchToProps = {
addItemToCart,
removeItem
}
export default connect(null, mapDispatchToProps) (ProductList);
Продукт
import React, { Component } from "react";
import {
View,
Text,
TouchableOpacity,
TextInput,
FlatList,
} from "react-native";
import Icon from "react-native-vector-icons/Ionicons";
class Products extends Component {
constructor(props) {
super(props);
const { products } = this.props;
this.state = {
products,
filteredProducts: products,
};
}
renderProducts = (products) => {
return (
<View key = {products.index}>
<View>
<Icon name = {products.item.icon} color = "#DD016B" size = {25} />
</View>
<View>
<Text style = {styles.name}>
{products.item.name}
</Text>
<Text>
€ {products.item.price}
</Text>
</View>
<View style = {styles.buttonContainer}>
<TouchableOpacity onPress = {() => this.props.addItemToCart(products.item)} >
<Icon name = "ios-add" color = "white" size = {25} />
</TouchableOpacity>
<TouchableOpacity onPress = {() => this.props.removeItem(products.item)} >
<Icon name = "ios-remove" color = "white" size = {25} />
</TouchableOpacity>
</View>
</View>
)
}
render() {
return (
<View>
<FlatList
style = {styles.listContainer}
data = {this.state.filteredProducts}
renderItem = {this.renderProducts}
keyExtractor = {(item, index) => index.toString()}
/>
</View>
);
}
}
export default Products;
Здравствуйте, я случайно отдал баллы другому человеку. Я могу исправить это?
Я не думаю, что это можно отменить. Это дает вам всплывающее предупреждение о присуждении награды. meta.stackexchange.com/questions/140290/…
мне ужасно жаль. Я был немного сбит с толку. :(
Нет проблем, я рад, что мой ответ смог вам помочь :-)
bindActionCreators — самая бессмысленная функция, просто передайте простой объект ключ-значение, и он будет упакован для вас в диспетчере.