简体   繁体   English

更改 props 时如何更新 React/recompose 组件?

[英]How to get React/recompose component updated when props are changed?

I'm writing this product list component and I'm struggling with states.我正在编写这个产品列表组件,但我正在为状态而苦苦挣扎。 Each product in the list is a component itself.列表中的每个产品本身都是一个组件。 Everything is rendering as supposed, except the component is not updated when a prop changes.一切都按预期渲染,除了在 prop 更改时组件不会更新。 I'm using recompose's withPropsOnChange() hoping it to be triggered every time the props in shouldMapOrKeys is changed.我正在使用 recompose 的withPropsOnChange()希望每次更改shouldMapOrKeys的道具时shouldMapOrKeys触发它。 However, that never happens.然而,这永远不会发生。

Let me show some code:让我展示一些代码:

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)

I don't think it's relevant but addToCart function returns a Promise.我认为这addToCartaddToCart函数返回一个 Promise。 Right now, it always resolves to true .现在,它总是解析为true

Another clarification: currentProducts and setProducts are respectively an attribute and a method from a class (model) that holds cart data.另一个说明: currentProductssetProducts分别是一个属性和一个方法,来自一个保存购物车数据的类(模型)。 This is also working good, not throwing exceptions or showing unexpected behaviors.这也很好用,不会抛出异常或显示意外行为。

The intended behavior here is: on adding a product to cart and after updating the currentProducts list, the addedToCart prop would change its value.这里的预期行为是:将产品添加到购物车并更新currentProducts列表后, addedToCart道具将更改其值。 I can confirm that currentProducts is being updated as expected.我可以确认currentProducts正在按预期更新。 However, this is part of the code is not reached (I've added a breakpoint to that line):但是,这是未到达代码的一部分(我在该行中添加了一个断点):

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

Since I've already used a similar structure for another component -- the main difference there is that one of the props I'm "listening" to is defined by withState() --, I'm wondering what I'm missing here.由于我已经为另一个组件使用了类似的结构——主要区别在于我“听”的一个道具是由withState()定义的——我想知道我在这里遗漏了什么. My first thought was the problem have been caused by the direct update of currentProducts , here:我的第一个想法是问题是由currentProducts的直接更新引起的,这里:

currentProducts.push(product.id)

So I tried a different approach:所以我尝试了一种不同的方法:

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

That didn't change anything during execution, though.不过,这在执行过程中没有任何改变。

I'm considering using withState instead of withPropsOnChange .我正在考虑使用withState而不是withPropsOnChange I guess that would work.我想那会奏效。 But before moving that way, I wanted to know what I'm doing wrong here.但在采取这种方式之前,我想知道我在这里做错了什么。

As I imagined, using withState helped me achieving the expected behavior.正如我想象的那样,使用withState帮助我实现了预期的行为。 This is definitely not the answer I wanted, though.不过,这绝对不是我想要的答案。 I'm anyway posting it here willing to help others facing a similar issue.无论如何,我在这里发布它愿意帮助面临类似问题的其他人。 I still hope to find an answer explaining why my first code didn't work in spite of it was throwing no errors.我仍然希望找到一个答案来解释为什么我的第一个代码尽管没有抛出任何错误但仍然不起作用。

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)

The changes here are:这里的变化是:

  1. Removed the withPropsOnChange , which used to handle the addedToCart "calculation";除去withPropsOnChange ,其用来处理addedToCart “计算”;
  2. Added withState to declare and create a setter for addedToCart ;添加withState来为addedToCart声明和创建一个 setter ;
  3. Started to call the setAddedToCart(true) inside the addToCart handler when the product is successfully added to cart;当产品成功添加到购物setAddedToCart(true)开始调用addToCart处理程序中的setAddedToCart(true)
  4. Added the componentWillReceiveProps event through the recompose's lifecycle to update the addedToCart when the props change.通过 recompose 的lifecycle添加componentWillReceiveProps事件以在道具更改时更新addedToCart

Some of these updates were based on this answer .其中一些更新基于此答案

I think the problem you are facing is due to the return value for withPropsOnChange .我认为您面临的问题是由于withPropsOnChange的返回值。 You just need to do:你只需要做:

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

As it happens with withProps , withPropsOnChange will automatically merge your returned object into props.withPropswithPropsOnChange会自动将返回的对象合并到 props 中。 No need of Object.assign() .不需要Object.assign()

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

ps: I would also replace the condition to be currentProducts.includes(product.id) if you can. ps:如果可以的话,我也会将条件替换为currentProducts.includes(product.id) It's more explicit.它更明确。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM