нежелательный рендеринг с использованием обработчиков реакции для управления состоянием - PullRequest
0 голосов
/ 13 июня 2019

Я новичок в реакции, и я пытаюсь создать простое приложение для корзины покупок без использования компонентов класса, узнав о хуках.В моем компонентном приложении верхнего уровня я получаю список элементов через импортированный json, затем создаю состояние с помощью useState для списка элементов, а также создаю редуктор для добавления или удаления элементов из корзины, которая имеет свое собственное состояние.Каждое состояние передается в компонент, ItemList вместе с функцией для отправки изменения состояния корзины (SpeiseListe в коде) и Cart.ItemList отображает список элементов в отдельные компоненты Item, у которых есть кнопка, которая запускает указанную отправку, и состояние перехватывания корзины в компоненте верхнего уровня обновляется.Теперь проблема заключается в том, что все приложение перерисовывается, как и ItemList, при каждом действии с корзиной.Я хотел бы обновить только компонент корзины, но я не уверен в правильном подходе.

(https://i.imgur.com/N6pfjvu.png)

Я не уверен, какое решение или концепция дляРешение может быть. Я думал о создании состояний в ItemList (SpeiseListe) и Cart соответственно, но тогда компонент Item должен был бы связаться со своими родными братьями и сестрами, и это не должно быть верно?

Приложение

import React, {useState, useReducer} from 'react'
import styled from 'styled-components'

import './bootstrap.min.css'

import Speisen from './Speisen'
import Cart from './Cart'

import * as speisen from './speisen.json'


export default function App() {
    const cartHandlerReducer = (cart, action) => {
        switch (action.type) {
            case 'ADD_TO_CART':
                return cart.concat(action.cartItem)
            case 'REMOVE_FROM_CART':
                return cart.filter(item => item.speise.id !== action.cartItem.speise.id)
            default:
                return cart
        }
    }

    const speisenNumbered = speisen.default.speisen.map((item, i) => ({...item, itemOrder: i + 1}))

    const [cart, dispatch] = useReducer(cartHandlerReducer, [])
    const [speiseListe, setSpeiseListe] = useState(speisenNumbered)

    const cartHandler = (cartItem) => {
        if (cart.find(item => item.speise.id === cartItem.speise.id)) {
            dispatch({type: 'REMOVE_FROM_CART', cartItem})
        } else {
            dispatch({type: 'ADD_TO_CART', cartItem})
        }
    }

    const MainContainer = styled.div.attrs({
        className: 'container'
    })``

    const Row = styled.div.attrs({
        className: 'row'
    })``

    return (
        <MainContainer>
            <Row>
                <Speisen
                    speisen={speiseListe}
                    cartHandler={cartHandler}
                />
                <Cart
                    cart={cart}
                />
            </Row>
        </MainContainer>
    )
}

Компонент отдельного элемента

import React, {useState} from 'react'
import styled from 'styled-components'


function Button(props) {
    return (
        <button
            onClick={props.onClick}
        >
            {props.added ?
                'remove' :
                'add'
            }
        </button>
    )
}


const SpeiseContainer = styled.div.attrs({
    className: 'row'
})`
    margin-bottom: 60px;
`

const OrderCol = styled.div.attrs({
    className: 'col-1'
})``

const NameCol = styled.div.attrs({
    className: 'col'
})``

const PreisCol = styled.div.attrs({
    className: 'col-2'
})`
    text-align: right;
`

const ButtonCol = styled.div.attrs({
    className: 'col-auto'
})``

export default function Speise (props) {
    const {speise, accomps, cartHandler} = props

    const [currentAccomp, setCurrentAccomp] = useState(accomps && accomps[0].id)

    const changeCurrentAccomp = (event) => setCurrentAccomp(accomps[event.target.value].id)

    const cartItemButton = () => {
        cartHandler({speise, accomps, currentAccomp})
    }

    console.log(currentAccomp)

    return (
        <SpeiseContainer>
            <OrderCol>
                {speise.itemOrder}
            </OrderCol>
            <NameCol>
                {speise.name}
                {currentAccomp && (
                    accomps.length > 1 ? (
                        <select
                            onChange={changeCurrentAccomp}
                        >
                            {accomps.map((accomp, i) => 
                                <option
                                    key={accomp.id}
                                    value={i}
                                >
                                    {accomp.name}
                                </option>
                            )}
                        </select>
                    ):(
                        accomps[0].name
                    )
                )}
            </NameCol>
            <PreisCol>
                {speise.price}
            </PreisCol>
            <ButtonCol>
                <Button onClick={cartItemButton} />
            </ButtonCol>
        </SpeiseContainer>
    )
}

Я хотел бы реализовать управление состояниями с помощью хуков, которые позволяют добавлять элементы в корзину без повторного отображения самого списка элементов только потому, что оба состоянияуправляется в одном и том же компоненте или по другому понимает, как это сделать вообще.

Большое спасибо!

1 Ответ

0 голосов
/ 13 июня 2019

Если данные списка элементов вообще не меняются, вы можете использовать React.memo , чтобы обернуть компонент ItemList, который будет выполнять поверхностное сравнение предыдущего и нового реквизита и предотвращать повторный рендеринг.если они равныТак что внутри вашего Speisen компонента вы можете просто экспортировать его так:

export default React.memo(Speisen);
...