4

我正在编写此产品列表组件,并且正在与状态作斗争。列表中的每个产品本身就是一个组件。一切都按预期呈现,除了当道具更改时组件没有更新。我正在使用 recompose,withPropsOnChange()希望每次shouldMapOrKeys更改道具时都会触发它。然而,这永远不会发生。

让我展示一些代码:

import React from 'react'
import classNames from 'classnames'
import { compose, withPropsOnChange, withHandlers } from 'recompose'
import { addToCart } from 'utils/cart'

const Product = (props) => {

  const {
    product,
    currentProducts,
    setProducts,
    addedToCart,
    addToCart,
  } = props

  const classes = classNames({
    addedToCart: addedToCart,
  })

  return (
    <div className={ classes }>
      { product.name }
      <span>$ { product.price }/yr</span>
      { addedToCart ?
        <strong>Added to cart</strong> :
        <a onClick={ addToCart }>Add to cart</a> }
    </div>
  )
}

export default compose(
  withPropsOnChange([
    'product',
    'currentProducts',
  ], (props) => {

    const {
      product,
      currentProducts,
    } = props

    return Object.assign({
      addedToCart: currentProducts.indexOf(product.id) !== -1,
    }, props)
  }),
  withHandlers({
    addToCart: ({
      product,
      setProducts,
      currentProducts,
      addedToCart,
    }) => {
      return () => {
        if (addedToCart) {
          return
        }
        addToCart(product.id).then((success) => {
          if (success) {
            currentProducts.push(product.id)
            setProducts(currentProducts)
          }
        })
      }
    },
  }),
)(Product)

我认为这无关紧要,但addToCart函数返回一个 Promise。现在,它总是解析为true.

另一个澄清:currentProductssetProducts分别是保存购物车数据的类(模型)的属性和方法。这也很好用,不会引发异常或显示意外行为。

这里的预期行为是:将产品添加到购物车并更新currentProducts列表后,addedToCart道具将更改其值。我可以确认currentProducts正在按预期更新。但是,这是部分代码未达到(我已在该行添加了一个断点):

return Object.assign({
  addedToCart: currentProducts.indexOf(product.id) !== -1,
}, props)

因为我已经为另一个组件使用了类似的结构——主要区别在于我“听”的一个道具是由withState()——定义的,我想知道我在这里缺少什么。我的第一个想法是问题是由直接更新引起的currentProducts,这里:

currentProducts.push(product.id)

所以我尝试了一种不同的方法:

const products = [ product.id ].concat(currentProducts)
setProducts(products)

不过,这在执行过程中并没有改变任何东西。

我正在考虑使用withState而不是withPropsOnChange. 我想那会奏效。但在这样做之前,我想知道我在这里做错了什么。

4

2 回答 2

0

正如我想象的那样,使用withState帮助我实现了预期的行为。不过,这绝对不是我想要的答案。无论如何,我在这里发布它愿意帮助面临类似问题的其他人。我仍然希望找到一个答案来解释为什么我的第一个代码尽管没有抛出任何错误却不起作用。

export default compose(
  withState('addedToCart', 'setAddedToCart', false),
  withHandlers({
    addToCart: ({
      product,
      setProducts,
      currentProducts,
      addedToCart,
    }) => {
      return () => {
        if (addedToCart) {
          return
        }
        addToCart(product.id).then((success) => {
          if (success) {
            currentProducts.push(product.id)
            setProducts(currentProducts)
            setAddedToCart(true)
          }
        })
      }
    },
  }),
  lifecycle({
    componentWillReceiveProps(nextProps) {
      if (this.props.currentProducts !== nextProps.currentProducts ||
          this.props.product !== nextProps.product) {
        nextProps.setAddedToCart(nextProps.currentProducts.indexOf(nextProps.product.id) !== -1)
      }
    }
  }),
)(Product)

这里的变化是:

  1. 删除了withPropsOnChange用于处理addedToCart“计算”的 ;
  2. 添加withState了为addedToCart;声明和创建 setter
  3. 商品成功加入购物车后开始调用setAddedToCart(true)内部处理程序;addToCart
  4. componentWillReceiveProps通过重组添加事件lifecycle以更新addedToCart道具更改时的事件。

其中一些更新基于此答案

于 2018-05-18T12:46:17.947 回答
0

我认为您面临的问题是由于withPropsOnChange. 你只需要这样做:

withPropsOnChange([
    'product',
    'currentProducts',
  ], ({
      product,
      currentProducts,
    }) => ({
      addedToCart: currentProducts.indexOf(product.id) !== -1,
    })
)

发生这种情况时withPropswithPropsOnChange会自动将您返回的对象合并到道具中。不需要Object.assign()

参考:https ://github.com/acdlite/recompose/blob/master/docs/API.md#withpropsonchange

ps:如果可以的话,我也会将条件替换为currentProducts.includes(product.id)。它更明确。

于 2020-01-15T16:01:33.907 回答