使用React钩子进行状态管理的不需要的渲染

时间:2019-06-13 00:28:55

标签: reactjs typescript react-hooks

我是一名新手,我在发现钩子之后尝试构建一个不使用类组件的简单购物车应用程序。 在我的顶级组件应用程序中,我通过导入的json获取项目列表,然后为项目列表创建一个带有useState的状态,并构建一个reducer以从具有自己状态的购物车中添加或删除项目。每个状态都将传递到组件,ItemList和一个用于分配购物车状态更改(代码中为SpeiseListe)和购物车的函数中。 ItemList将项目列表映射到单个Item组件,该组件具有一个按钮,onClick会触发该调度,并且顶层组件中的购物车挂钩状态会更新。 现在的问题是,在涉及购物车的每个操作中,整个App都会重新渲染,ItemList也会重新渲染。我只想更新购物车组件,但是我不确定正确的方法。

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

我不确定解决方案或解决方案的概念是什么。我当时正在考虑分别在ItemList(SpeiseListe)和Cart中创建状态,但是随后Item Component需要与其父级兄弟进行通信,这不应该对吗?

应用

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>
    )
}

我想使用Hooks实现状态管理,这使得可以将商品添加到购物车而无需重新呈现商品列表本身,因为这两个状态都在同一组件中或如何执行此操作的不同概念中进行管理完全没有。

非常感谢!

1 个答案:

答案 0 :(得分:0)

如果物品列表数据完全没有改变,则可以使用React.memo包装ItemList组件,该组件将对先前道具和新道具进行浅比较,并在它们相等时防止重新渲染。因此,在Speisen组件内部,您可以像这样简单地将其导出:

export default React.memo(Speisen);
相关问题