父级的React道具不会传递给子组件

时间:2018-02-21 10:49:06

标签: reactjs redux

我将道具传递给子组件,来自父组件,从他父母那里接收同一道具。

出于某种原因,当父道具更新时,此更新不会影响子组件。

组件本身非常基础:

这是父母:

import React, { Component } from 'react'
import styled from 'styled-components'
import { Icon } from 'antd'

import BaseProductSelector from '../BaseProductSelector'
import BaseProductPreview from '../BaseProductPreview'
import FullDesignSelector from '../FullDesignSelector'
import ColorPicker from '../ColorPicker'
import GeneratorProgress from '../GeneratorProgress'
import GeneratorError from '../GeneratorError'
import BPDetails from './BPDetails'

const GeneratorFlow = styled.div`
  /*background-color: #eeeeee;*/
  padding: 0 60px;
  h3 {
    color: #444;
  }
  .innerGenFlow {
    padding: 15px;
    border-radius: 5px;
    box-shadow: 0 20px 40px -14px rgba(0, 0, 0, 0.35);
  }
`

export default class ProductGeneratorFlow extends Component {
  constructor(props) {
    super()

    let colors
    if (props.product) {
      colors = props.product.variations.filter(p => p.type === 'color')
    } else {
      colors = []
      // colors.push(props.baseProduct.variations.filter(p => p.type === 'color')[0])
    }

    this.state = {
      pickedColors: colors,
      done: false,
      error: false,
    }
    this.setStep = this.setStep.bind(this)
    this.setKey = this.setKey.bind(this)
    this.showBP = this.showBP.bind(this)
    this.hideBP = this.hideBP.bind(this)
    this.onChange = this.onChange.bind(this)
    this.toggleColor = this.toggleColor.bind(this)
    this.toggleAll = this.toggleAll.bind(this)
    this.productCreated = this.productCreated.bind(this)
    this.productPending = this.productPending.bind(this)
    this.setFirstColor = this.setFirstColor.bind(this)
    this.displayError = this.displayError.bind(this)
  }

  setStep(step) {
    this.setState({ step })
  }

  showBP() {
    this.setState({ BPDisplayed: true })
  }

  hideBP() {
    this.setState({ BPDisplayed: false })
  }

  getBaseProd() {
    const bpid = this.props.product.supplierBaseProductId
    const result = this.props.base_products.filter(obj => obj._id === bpid)
    return result[0]
  }

  setKey(activeKey) {
    this.setState({
      activeKey,
    })
  }

  onChange(activeKey) {
    this.setState({
      activeKey,
    })
  }

  productPending() {
    this.setState({
      done: false,
      error: false,
    })
    this.props.showBP()
  }

  productCreated() {
    this.props.displaySuccess()
    this.setState({ done: true })
  }

  displayError() {
    this.setState({ error: true })
  }

  toggleColor(color) {
    let pickedColors = this.state.pickedColors
    if (this.state.pickedColors.includes(color)) {
      // console.log(pickedColors.filter(i => i != color).length)
      pickedColors = pickedColors.filter(i => i != color)
    } else {
      pickedColors.push(color)
    }
    this.setState({
      pickedColors,
    })
  }

  test(id) {
    this.setState({picked: true})
    this.props.select(id)
  }

  toggleAll(value) {
    if (value === true) {
      this.setState({
        pickedColors: this.props.baseProduct.variations.filter(p => p.type === 'color'),
      })
    } else {
      this.setState({ pickedColors: [] })
    }
  }

  setFirstColor() {
    if (this.state.pickedColors.length > 0) {
      this.props.setVariation(this.state.pickedColors[0])
    }
  }

  render() {
    if (this.state.error) {
      return (
        <GeneratorError
          showBP={this.props.showBP}
          reset={this.productPending}
        />
      )
    }
    if (this.state.done) {
      return (
        <GeneratorProgress
          active
          showBP={this.props.showBP}
          reset={this.productPending}
        />
      )
    }
    if (this.props.product) {
      return (
        <GeneratorFlow>
          <FullDesignSelector
            designs={this.props.designs}
            select={this.test}
            addedDesigns={this.props.addedDesigns}
            getImage={this.props.getImage}
            removeDesign={this.props.removeDesign}
            active
            printingZone={this.props.printingZone}
            setStep={this.setStep}
            showBP={this.showBP}
            showDS={this.props.showDS}
            setKey={this.setKey}
          />
          <ColorPicker
            baseProduct={this.props.baseProduct}
            product={this.props.product}
            picked={this.state.pickedColors}
            toggleColor={this.toggleColor}
            variation={this.props.variation}
            selectAll={this.toggleAll}
            toggleFirstColor={this.props.toggleFirstColor}
            setVariation={this.props.setVariation}
            selectedColor={
              this.props.variation ? this.props.variation.value : null
            }
            setPreviewColor={this.props.setVariation}
          />
          <BaseProductPreview
            addedDesigns={this.props.addedDesigns}
            size={this.props.size}
            shop={this.props.shop}
            printingZone={this.props.printingZone}
            picked={this.state.pickedColors}
            previews={this.props.previews}
            product={this.props.product}
            setDone={this.productCreated}
            baseProduct={this.getBaseProd()}
            displaySuccess={this.props.displaySuccess}
            generatorError={this.displayError}
            status='edition'
            setKey={this.setKey}
            products={this.props.products}
            productLoading={this.props.productLoading}
          />
        </GeneratorFlow>
      )
    }
    return (
      <GeneratorFlow>
        <ColorPicker
          picked={this.state.pickedColors}
          toggleColor={this.toggleColor}
          baseProduct={this.props.baseProduct}
          toggleFirstColor={this.setFirstColor}
          variation={this.props.variation}
          selectAll={this.toggleAll}
          setVariation={this.props.setVariation}
          selectedColor={
            this.props.variation ? this.props.variation.value : null
          }
          setPreviewColor={this.props.setVariation}
        />
        <FullDesignSelector
          designs={this.props.designs}
          select={this.props.select}
          addedDesigns={this.props.addedDesigns}
          getImage={this.props.getImage}
          printingZone={this.props.printingZone}
          removeDesign={this.props.removeDesign}
          active
          setStep={this.setStep}
          showBP={this.showBP}
          showDS={this.props.showDS}
          setKey={this.setKey}
        />
        <BaseProductPreview
          addedDesigns={this.props.addedDesigns}
          baseProduct={this.props.baseProduct}
          generatorError={this.displayError}
          size={this.props.size}
          displaySuccess={this.props.displaySuccess}
          shop={this.props.shop}
          picked={this.state.pickedColors}
          setDone={this.productCreated}
          printingZone={this.props.printingZone}
          previews={this.props.previews}
          setPreview={this.props.setPreview}
          status='creation'
          setStep={this.setStep}
          products={this.props.products}
          productLoading={this.props.productLoading}
        />
      </GeneratorFlow>
    )
  }
}

这是在孩子中使用这个道具的唯一部分

import React, { Component } from 'react'
import styled from 'styled-components'
import toPx from 'unit-to-px'
import _ from 'lodash'
import { LocalForm, Control } from 'react-redux-form'
import { withRouter } from 'react-router-dom'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { toast } from 'react-toastify'
import { Input, Select, Icon, Tooltip } from 'antd'
import s3 from '../../../../../services/s3'
import theme from '../../../../../theme/theme'
import Alert from '../../../../../components/Alert'
import { createProduct, modifyProduct } from '../../../../../modules/products'

// import ProductImage from '../../../../../components/ProductImage'


class BaseProductPreview extends Component {
  constructor(props) {
    super(props)

    let colors
    if (props.product) {
      colors = Object.assign([], props.product.variations.filter(v => v.type === 'color'))
    } else {
      colors = []
    }
    this.state = {
      name: props.product ? props.product.name : '',
      displayDescription: props.product ? Object.assign({}, props.product.displayDescription) : {},
      collections: (props.product && props.product.collections) ? props.product.collections : [],
      pricing: props.product ? Object.assign({}, props.product.pricing) : { margin: 0 },
      elevdone: false,
      i: 0,

      colors,
    }
    this.createInnerProduct = this.createInnerProduct.bind(this)
    this.getPrice = this.getPrice.bind(this)
  }

  oneColor() {
    if (this.props.baseProduct.variations && this.state.colors) {
      let colorAlert
      if (this.state.colorAlertShown) {
        colorAlert = <Alert message='Choisissez au moins une couleur' type='error' />
      }
      const colorsBp = this.props.baseProduct.variations.filter(v => v.type === 'color')
      if (colorsBp.length <= 1) {
        return ''
      }
      return (
        <div>
          <p>Choix de couleur :</p>
          { colorAlert }
          <span
            className='bullet-color'
          >
            {this.getColorsRef(this.props.baseProduct).map(value =>
              (<div
                onClick={() => { this.toggleColor(value) }}
                className={this.colorIsInProduct(value)}
                style={{ backgroundColor: value }}
              />))}
          </span>
        </div>)
    }
    return null
  }


  getColorsRef() {
    return this.props.baseProduct.variations.filter(v => v.type === 'color').map(a => a.value)
  }

  colorIsInProduct(couleur) {
    // true/false
    let active
    if (this.state.colors.find(v => v.value === couleur)) {
      active = 'active-color'
    }
    return active
  }
  toggleColor(couleur) {
    // const item = this.state.item
    const colors = this.state.colors
    // si on a deja la couleur dans le produit on l'enlève
    if (colors.find(v => v.value === couleur) && colors.length > 1) {
      // je retire cette couleur des varaitions de mon produits
      const index = colors.indexOf(colors.find(v => v.value === couleur))
      colors.splice(index, 1)
    } else if (colors.find(v => v.value === couleur)) {
      this.setState({ colorAlertShown: true })
    } else {
      // on va chercher la variation couleur corespondante
      // dans le base product et on la copie dans le product
      this.setState({ colorAlertShown: false })
      colors.push(this.props.baseProduct.variations.find(v => v.value === couleur))
    }
    this.setState({ colors })
    // TODO on change la couleur du mockup
  }

  getJsonsObjects(printingZones) {
    // INITIATE EMPTY JSON OBJECT
    const jsonsArray = {}

    // GET CURRENT CANVAS

    printingZones.map((item) => {
      const y = document.getElementById(`${item}-canvas`).fabric
      const helper = _.filter(y.getObjects(), { clipFor: 'layer' })[0]
      if (helper) {
        helper.set({ stroke: 'transparent' })
      }
      jsonsArray[item] = y.toJSON(['height'])
    })
    return jsonsArray
  }



  getCustomizationPrice() {
    let customizationPrice = 0
    Object.keys(this.props.baseProduct.printingZone).map((item) => {
      const y = document.getElementById(`${item}-canvas`).fabric
      const items = y.getObjects()
      if (items.length > 1) {
        customizationPrice = customizationPrice + 5
      }
    })
    customizationPrice = customizationPrice - 5
    if (customizationPrice < 0) {
      customizationPrice = 0
    }
    return customizationPrice
  }

  getAction() {
    return (<p>Créer mon produit</p>)
  }

  marginValidation(value) {
    let returned_value = value
    if (value == '') {
      returned_value = 0
    }
    if (!value) {
      returned_value = 0
    } else if (value > 100) {
      // TODO Show moreThan100Alert
      returned_value = 100
    }
    const pricing = Object.assign({}, this.state.pricing, { margin: returned_value })

    this.setState({ pricing })
  }
  validForm() {
    if (this.state.name && this.props.picked.length > 0 && this.state.pricing.margin >= 0 && this.props.addedDesigns.length > 0) {
      return false
    }
    return true
  }
  getPrice() {
    const position_print = this.props.addedDesigns.map(d => {
      return d.position
    })
    // uniq(position_print)
    const count = []
    position_print.map((position) => {
      if (count.indexOf(position) === -1) {
        count.push(position)
      }
    })
    if (count.length <= 1) {
      return (
        <div>
          <p className='price'>Cout de production <span>{this.props.baseProduct.unitPrice} €</span></p>
          <div className='price-marge'>Vos bénéfices <span className='requiredField2'>*</span>
            <Control.text
              component={Input}
              className='inputMarge'
              model='.margin'
              value={this.state.pricing.margin}
              onChange={(e) => {
                this.marginValidation(e.target.value.replace(',', '.'))
              }}
            />
          </div>
          <hr />
          <div className='price-total'>
            {`
        ${parseFloat(this.props.baseProduct.unitPrice)
        +
        parseFloat(this.state.pricing.margin)} €`}
          </div>
        </div>
      )
    }
    if (count.length > 1) {
      return (
        <div>
          <p className='price'>Cout de production <span>{this.props.baseProduct.unitPrice} €</span></p>
          <p className='price'>Impression supplémentaire <span>5 €</span></p>
          <div className='price-marge'>Vos bénéfices <span className='requiredField2'>*</span>
            <Control.text
              component={Input}
              className='inputMarge'
              model='.margin'
              value={this.state.pricing.margin}
              onChange={(e) => {
                this.marginValidation(e.target.value.replace(',', '.'))
              }}
            />
          </div>
          <hr />
          <div className='price-total'>
            {`
          ${parseFloat(this.props.baseProduct.unitPrice)
          +
          parseFloat(this.state.pricing.margin) + parseFloat(5)} €`}
          </div>
        </div>
      )
    }
    return null
  }

  getCategory() {
    if (this.props.baseProduct.category.fr[0] === 'Homme' && this.props.baseProduct.category.fr[1] === 'Femme') {
      return (<span>Unisex</span>)
    }
    if (this.props.baseProduct.category.fr[0] === 'Homme') {
      return (<span>Homme</span>)
    }
    if (this.props.baseProduct.category.fr[0] === 'Femme') {
      return (<span>Femme</span>)
    }
    return null
  }
  showElevio() {
    if (this.state.i < 5) {
      this.state.i = this.state.i + 1
      setTimeout(() => {
        if (this.props.productLoading.loading === true || this.props.products.length === 0) {
          if (this.props.products.length === 0 && this.state.elevdone === false) {
            return (
              window._elev.openArticle(263),
              this.setState({ elevdone: true })
            )
          } return null
        }
        if (this.props.productLoading.loading === false) {
          this.showElevio()
        }
        return null
      }, 500)
    } return null
  }
  render() {
    const { Option } = Select
    const children = []
    if (this.props.shop.settings.collections) {
      this.props.shop.settings.collections.map((collec, i) => {
        children.push(<Option key={collec.name ? collec.name : i}>{collec.name}</Option>)
        return null
      })
    }
    this.showElevio()
    return (
      <StyledBaseProductPreview>
        <h2>Description</h2>
        <LocalForm
          onSubmit={() => this.createInnerProduct()}
        >
          <div className='form-step'>
            <p className='advice-name'>
              <Tooltip title='Figurera sur la fiche produit'>
                <span>{this.props.baseProduct.subCategory.fr} {this.getCategory()}</span>
              </Tooltip>
            </p>
            <p>Nom <span className='requiredField'>*</span></p>
            <Control.text
              component={Input}
              model='.name'
              placeholder='Nom du produit'
              value={this.state.name}
              onChange={(e) => {
                this.setState({ name: e.target.value })
              }}
            />
          </div>
          <div className='form-step'>
            <p>Description</p>
            <Control.textarea
              className='productDescription'
              model='.displayDescription'
              placeholder='Description du produit'
              value={this.state.displayDescription.fr}
              onChange={(e) => {
                const new_item = Object.assign({}, this.state.displayDescription)
                new_item.fr = e.target.value
                this.setState({ displayDescription: new_item })
              }}
            />
          </div>

          <div className='form-step'>
            <p>Collection(s)</p>
            <Select
              mode='multiple'
              className='styledSelect'
              placeholder='Pas de collection'
              notFoundContent='Pas de collection'
              value={this.state.collections}
              style={{ width: '100%' }}
              onSearch={(e) => {
                this.setState({ toCreate: e })
              }}
              onChange={(e) => {
                this.setState({ collections: e })
              }}
            >
              {children}
            </Select>
          </div>
          <div className='form-step pricingForm'>
            <h2>Prix </h2>
            <hr />
            {this.getPrice()}
          </div>
          <Crumpet type='submit' className='superCrumpet' disabled={this.validForm()}>
            {this.getAction()}
          </Crumpet>
        </LocalForm>
      </StyledBaseProductPreview>
    )
  }
}

const mapStateToProps = (state, ownProps) => {
  console.log(state); // state
  console.log(ownProps); // undefined
  return({
    user: state.user,
    addedDesigns: ownProps.addedDesigns,
  })
}

const mapDispatchToProps = dispatch => bindActionCreators({
  createProduct,
  modifyProduct,
}, dispatch)

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(BaseProductPreview)

检查这些元素时,我可以看到父级正在更新:

Parent props

现在,有趣的是:当父道具更新时,前两个子组件道具也会更新,但BaseProductPreview不会更新!

Child props

但是,只要更改子组件的状态,就会立即更新。

怎么会这样?状态如何更新组件道具?

2 个答案:

答案 0 :(得分:0)

React只对不在道具上的状态起作用。每当您更改状态UI时,都会使用最新更改进行更新将您的道具绑定到子组件中的状态,并使用componentWillReceiveProps(props)在父组件中更新道具时更改子状态。

答案 1 :(得分:0)

我找到的唯一解决方案是将更高的组件与redux连接起来,这样就不会有任何子项与redux相关,然后通过props将reducer函数传递给那个孩子。

如果没有redux,我的组件会正确更新。

相关问题