React-Redux | Список продуктов | Добавить/удалить с помощью BindActionCreator

Я пытаюсь создать список продуктов в 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)

bindActionCreators — самая бессмысленная функция, просто передайте простой объект ключ-значение, и он будет упакован для вас в диспетчере.

Dominic 12.02.2019 12:25
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
2
1
663
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Код выглядит нормально по большей части.

Часть подключения реакции-редукции в ProductList выглядит отключенной. количество всегда равно 0. Должно быть 1.

Также должен присутствовать mapStateToProps для получения товаров из корзины.

Спасибо за Ваш ответ. Кол-во товара начинается с 0, потому что в данный момент в корзине нет товара. Я думал, но я проверю это сегодня вечером. Что касается mapStateToProps, не могли бы вы объяснить немного больше?

Salman 12.02.2019 12:59

В вашем 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;

Поиграйте с этим и посмотрите, как у вас получится.

Спасибо за Ваш ответ. Вечером посмотрю более внимательно :)

Salman 12.02.2019 12:58
Ответ принят как подходящий

У вас есть два разных действия, 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;

Здравствуйте, я случайно отдал баллы другому человеку. Я могу исправить это?

Salman 13.02.2019 12:16

Я не думаю, что это можно отменить. Это дает вам всплывающее предупреждение о присуждении награды. meta.stackexchange.com/questions/140290/…

Shubham Khatri 13.02.2019 12:24

мне ужасно жаль. Я был немного сбит с толку. :(

Salman 13.02.2019 20:02

Нет проблем, я рад, что мой ответ смог вам помочь :-)

Shubham Khatri 14.02.2019 06:28

Другие вопросы по теме