ddimonn8080
@ddimonn8080

Как удалять пункт списка в react/redux?

В компоненте формы с комментариями такой код
import React, {Component} from 'react';
import ReactHtmlParser from "react-html-parser";
import {validateEmail} from '../../../../helpers/validation';
import Tabs from './Tabs';
import ProductComments from '../ProductComments';

class ProductTabs extends Component {

    constructor(props){
        super(props);
        this.state = {
            comments: [],
            users: {},
            commentsLength: 0,
            /* states for creating new comment */
            productCommentContent: '',
            productSlug: this.props.productSlug,
            productID: this.props.productID,
            userID: null,
            userName: '',
            userEmail: '',
        };
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
        this.handleRemove = this.handleRemove.bind(this);
    }

    componentDidMount(){

        console.log( 'this.props', this.props );

        const {productSlug, setProductComments} = this.props;

        axios.get(`/api/product-comments/${productSlug}`).then(data => {

            setProductComments(data.data);

            let {allComments, allUsers} = data.data;
            const users = allUsers.reduce((acc, el) => (
                acc[el.id] = el, acc
            ), {});

            const USER_ID = this.state.userID ? this.state.userID : 11

            this.setState({
                comments: allComments,//is Array
                users: users,// is Object
                userID: USER_ID,
                userName: (USER_ID === 11) ? '' : users[USER_ID]['name'],
                userEmail: users[USER_ID]['email'],
                userLogo: users[USER_ID]['logo'],
                commentsLength: allComments.length ? allComments.length : 0,
            });
        });
    }

    handleChange(e){
        this.setState({
            [`${e.target.name}`]: e.target.value,
        });
    }

    handleSubmit(e){
        e.preventDefault();

        if ( this.state.productCommentContent && validateEmail(this.state.userEmail) && this.state.userName ) {
            const productComment = {
                content: this.state.productCommentContent,
                product_slug: this.state.productSlug,
                product_id: this.state.productID,
                user_id: this.state.userID,
                user_name: this.state.userName,
                user_email: this.state.userEmail,
            }

            axios.post('/api/product-comments', productComment).then(response => {
                const newCommentsList = [...this.state.comments, response.data];
                this.setState({
                    comments: newCommentsList,
                    commentsLength: newCommentsList.length,
                });
            });
        }
    }

    handleRemove(id){
        const {removeProductCommentById} = this.props;

        const newCommentsList = this.state.comments.filter(comment => comment.id !== id);

        this.setState({
            comments: newCommentsList,
            commentsLength: newCommentsList.length,
        });

        removeProductCommentById(id);
    }

    render(){
        const {tabBg, descr, ingredients, usage, title } = this.props;

        return (
            <Tabs tabBg={tabBg}>
                {descr && (
                    <div title="Описание">
                        {ReactHtmlParser(descr)}
                    </div>
                )}

                {ingredients && (
                    <div title="Состав">
                        {ReactHtmlParser(ingredients)}
                    </div>
                )}

                {usage && (
                    <div title="Применение">
                        {ReactHtmlParser(usage)}
                    </div>
                )}

                <div title={`Отзывы (${this.state.commentsLength})`}>
                    <ProductComments
                        title={title}
                        state={this.state}
                        handleChange={this.handleChange}
                        handleSubmit={this.handleSubmit}
                        handleRemove={this.handleRemove}
                    />
                </div>
            </Tabs>
        );
    }
}

export default ProductTabs;

actions/products.js

import {
    SET_PRODUCTS,
    SET_PRODUCT_BY_SLUG,
    SET_PRODUCT_BY_SLUG_SUCCEEDED,
    SET_PRODUCT_BY_SLUG_FAILED,
    SET_PRODUCT_COMMENTS,
    REMOVE_COMMENT_BY_ID,
    REMOVE_COMMENT_BY_ID_SUCCEEDED,
    SHOW_ALL,
    BESTSELLER,
    FACE,
    BODY,
    SCRUB
} from './types';

export const setProducts = products => ({
    type: SET_PRODUCTS,
    payload: products
});

export const setProductComments = comments => ({
    type: SET_PRODUCT_COMMENTS,
    payload: comments
});

export const removeProductCommentById = id => {
    return async dispatch => {
        axios.delete(`/api/product-comments/${id}`).then(() => {
            dispatch({ type: REMOVE_COMMENT_BY_ID, payload: id });
        });
    };
};

export const setProductBySlug = slug => {
    return async dispatch => {
        dispatch({ type: SET_PRODUCT_BY_SLUG });

        axios.get(`/api/products/${slug}`)
            .then(({data}) => {
                if(data.product){
                    dispatch({ type: SET_PRODUCT_BY_SLUG_SUCCEEDED, payload: data });
                } else {
                    dispatch({ type: SET_PRODUCT_BY_SLUG_FAILED, payload: 404 });
                }
            }).catch(err => {
                dispatch({ type: SET_PRODUCT_BY_SLUG_FAILED, payload: err });
            });
    };
}

export const CategoryFilters = {
    SHOW_ALL: SHOW_ALL,
    BESTSELLER: BESTSELLER,
    FACE: FACE,
    BODY: BODY,
    SCRUB: SCRUB,
};

reducers/products.js

import {
    SET_PRODUCTS,
    SET_PRODUCT_BY_SLUG,
    SET_PRODUCT_BY_SLUG_SUCCEEDED,
    SET_PRODUCT_BY_SLUG_FAILED,
    SET_PRODUCT_COMMENTS,
    REMOVE_COMMENT_BY_ID,
} from '../actions/types';

const INITIAL_STATE = {
    isReady: false,
    isSingleReady: false,
    isSingleLoading: false,
    isCommentsReady: false,
    items: [],
    product: {},
    comments: [],
    error: null,
};

export default function (state = INITIAL_STATE,action){
    switch (action.type) {
        case SET_PRODUCTS:
            return {
                ...state,
                items: action.payload,
                isReady: true
            };
        case SET_PRODUCT_BY_SLUG:
            return {
                ...state,
                isSingleReady: false,
                isSingleLoading: true,
                error: null,
            };

        case SET_PRODUCT_BY_SLUG_SUCCEEDED:
            return {
                ...state,
                product: action.payload,
                isSingleReady: true,
                isSingleLoading: false,
                error: null,
            };

        case SET_PRODUCT_BY_SLUG_FAILED:
            return {
                ...state,
                isSingleReady: false,
                isSingleLoading: false,
                error: action.payload,
            };

        case SET_PRODUCT_COMMENTS:
            return {
                ...state,
                comments: action.payload,
                isCommentsReady: true,
            };
        case REMOVE_COMMENT_BY_ID:
            return {
                ...state,
                allComments: state.comments.allComments.filter(comment => comment.id != action.payload),
            };
        default:
            return state;
    }
}

Вопрос в том что при удалении комментария я сначала фильтрую состояние компонента а потом через removeProductCommentById(id); меняю store. Так правильно делать либо все должно меняться через store?

PS При изменении состояния с помощью setState() происходит ререндер компонента, а происходит ли ререндер при изменении состояния в store ?

Спасибо.
  • Вопрос задан
  • 103 просмотра
Решения вопроса 1
rockon404
@rockon404 Куратор тега React
Frontend Developer
Главная проблема вашей логики в том, что вы используете два источника правды для одних и тех же данных: локальное состояние и хранилище redux. Непонятно зачем вы вообще храните эти данные в локальном состоянии компонента.

По-хорошему, ваш запрос за комментариями надо вынести в асинхронное действие, а данные прокидывать в компонент из store вызовом connect.

class Example extends React.Component {
  componentDidMount() {
    const { fetchComments, productSlug } = this.props;
    fetchProductCommentsBySlug(productSlug);
  }

  handleRemoveComment = id => {
    this.props.removeProductCommentById(id); 
  };

  handleSubmitComment = e => {
    // ...
    this.props.postProductComment(productComment);
  };
  
  render() {
    const { comments, users } = this.props;
    // ...
  }
}

const mapStateToProps = state => ({
  users: usersSelector(state),
  comments: commentsSelector(state),
});

const mapDispatchToProps = {
  fetchProductCommentsBySlug,
  removeProductCommentById,
  postProductComment,
};

connect(mapStateToProps, mapDispatchToProps)(Example);
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы