即使存储已更新,Redux也不会​​重新呈现React组件

时间:2017-07-23 17:37:22

标签: reactjs redux react-redux redux-form

您好我是Redux的新手,我使用React和Redux尝试构建一个UI,我可以将文件(在这种情况下为发票)拖放到UI的一部分中,渲染它们在列表中,然后能够启动弹出框以编辑与每个发票关联的元数据。拖放工作正常 - Redux每次删除文件并更新列表时都会重新渲染视图。但是,当我尝试单击每个发票的编辑按钮时,商店正在更新,但我的popover组件中的道具却没有。实际上,当我尝试点击编辑发票按钮时,看起来根本不会发生任何重新渲染

App.js

import React from 'react'
import AddInvoice from '../containers/AddInvoice'
import CurrentInvoiceList from '../containers/CurrentInvoiceList'
import ControlPopover from '../containers/ControlPopover'

const App = () => (
  <div>
    <AddInvoice />
    <CurrentInvoiceList />
    <ControlPopover />
  </div>
)

export default App

容器/ AddInvoice.js

import React from 'react'
import { connect } from 'react-redux'
import { addInvoice } from '../actions'

const recipientDataDefaults = {
  name: '',
  surname: '',
  address: '',
  phone: ''
};

const handleDragOver = event => {
  event.stopPropagation();
  event.preventDefault();
  event.dataTransfer.dropEffect = 'copy';
};

const handleDragEnter = event => {
  event.stopPropagation();
  event.preventDefault();
};

const handleDragLeave = event => {
  event.stopPropagation();
  event.preventDefault();    
};

let AddInvoice = ({ dispatch }) =>
  const styles = {'minHeight': '200px', 'background': 'tomato'}
  return (
    <div style={styles}
         onDragEnter={handleDragEnter} 
         onDragLeave={handleDragLeave} 
         onDragOver={handleDragOver} 
         onDrop={event => {
           event.stopPropagation(); 
           event.preventDefault();
           const data  = event.dataTransfer;
           const files = data.files;

           const newInvoiceUploads = Object.keys(files)
             .map(key => files[key])
             .map(file => {
               const invoiceObject = {};
               invoiceObject.files = [file];
               invoiceObject.recipientData = Object.assign({}, recipientDataDefaults);
               return invoiceObject;
             });

           newInvoiceUploads.forEach(invoice => dispatch(addInvoice(invoice)))

    }}>
      Drag an invoice here to upload
    </div>
  )
}
AddInvoice = connect()(AddInvoice)

export default AddInvoice

容器/ ControlPopover.js

import { connect } from 'react-redux'
import { closePopoverWithoutSave } from '../actions'
import Popover from '../components/Popover/Popover'

const mapStateToProps = (state) => {
  return {
    isActive: !!state.isActive
  }
}

const mapDispatchToProps = {
  handleCancel: closePopoverWithoutSave
}


const ControlPopover = connect(
  mapStateToProps,
  mapDispatchToProps
)(Popover)

export default ControlPopover

容器/ CurrentInvoiceList.js

import { connect } from 'react-redux'
import { showInvoiceEditPopover } from '../actions'
import InvoiceList from '../components/InvoiceList/InvoiceList'

const mapStateToProps = state => {
  return {
    invoices: state.invoices
  }
}

const mapDispatchToProps = dispatch => ({
  handleEditInvoice: invoice => {
    dispatch(showInvoiceEditPopover(invoice))
  }
})

const CurrentInvoiceList = connect(
  mapStateToProps,
  mapDispatchToProps
)(InvoiceList)

export default CurrentInvoiceList

动作/ index.js

let nextInvoiceId = 0
export const addInvoice = invoice => ({
  type: 'ADD_INVOICE',
  id: nextInvoiceId++,
  invoiceData: invoice
})

export const showInvoiceEditPopover = invoice => ({
  type: 'SHOW_POPOVER',
  invoice
})

popover reducer(在app中合并,但为了简洁而在此处内联)reducers / index.js

const popover = (state = {}, action) => {
  switch (action.type) {
    case 'SHOW_POPOVER':
      const popoverState = {}
      popoverState.isActive = true
      popoverState.data = action.invoice
      return popoverState

     case 'CLOSE_POPOVER_WITHOUT_SAVING':
       const inactiveState = {}
       inactiveState.isActive = false
       inactiveState.data = {}
       return inactiveState;
     default:
       return state
   }
}

export default popover

组件/ InvoiceList.js

import React from 'react'
import PropTypes from 'prop-types'
import Invoice from '../Invoice/Invoice'

const InvoiceList = ({ invoices, handleEditInvoice }) => {

return (
  <div>
    {invoices.map(invoice =>
      <Invoice
        key={invoice.id}
        invoice={invoice.invoiceData}
        onClick={event => {
          // here we invoke the action bound by the CurrentInvoiceList
          // container
          event.preventDefault()
          handleEditInvoice(invoice)
        }}
      />
    )}
  </div>
 )
}

InvoiceList.propTypes = {
  invoices: PropTypes.arrayOf(PropTypes.shape({
    id: PropTypes.number.isRequired,
    invoiceData: PropTypes.object
  }).isRequired).isRequired,
  handleEditInvoice: PropTypes.func.isRequired
}

export default InvoiceList

组件/ Invoice.js

import React from 'react'
import PropTypes from 'prop-types'
import TextInput from '../TextInput/TextInput';
import Button from '../Button/Button';
import './invoice.css'

const Invoice = ({ invoice, onClick }) => {

const fileNames = invoice.files.map((file, index) => {
    return (<div key={index} className="invoice__file-title-legend">
   {file.name}</div>);
  });

return (
    <div className="invoice">
    <form className="invoice__form">
    <TextInput id="invoice__input-amount" placeholder="enter invoice amount" label="Invoice Amount" />
    <TextInput id="invoice__input-target" placeholder="enter payment target" label="Payment Target" />
    <Button value="Add recipient" onClick={onClick} /> // clicking this button updates the store but does NOT re-render. Why?
  </form>

  <div className="invoice__files">{fileNames}</div>
</div>
  )
}

Invoice.propTypes = {
  onClick: PropTypes.func.isRequired,
  invoice: PropTypes.object
}

export default Invoice

组件/酥料饼/ Popover.js

import React from 'react'
import PropTypes from 'prop-types'
import createModifiers      from '../../lib/createModifiers';
import './Popover.css';

const Popover = ({ handleCancel, isActive }) => {
  console.log('rendering popover component') // does not get called when invoice edit button is pressed
  const popoverModifiers = createModifiers('popover', {
    'is-active': isActive
  })

  return (
    <div className={popoverModifiers}>
      <div className="popover__header">
        <button onClick={handleCancel}>x</button>
      </div>
      <div className="popover__content">
       Popover content
      </div>
    </div>
  )
}
Popover.propTypes = {
  handleCancel: PropTypes.func.isRequired,
  isActive: PropTypes.bool.isRequired 
}

export default Popover

最后是后代的createModifiers代码。此代码仅基于在

中传递的状态布尔值生成一些BEM修饰符CSS类
const createModifiers = (element, modifiers) => {
  const classList = Object.keys(modifiers)
    .filter(key => modifiers[key])
    .map(modifier => `${element}--${modifier}`)
    .concat(element)
    .join(' ')

  return classList
}

export default createModifiers

我知道这是一个大量的示例代码,因此我尽量保持简洁和集中,同时提供应用程序的全面视图。非常感谢任何帮助。

2 个答案:

答案 0 :(得分:0)

我相信您的问题是您的BYTE PTR功能格式不正确。

您需要返回一个包含方法的对象。这些方法将作为道具提供给您连接的组件。

示例:

mapDispatchToProp

const mapDispatchToProps = ( dispatch ) => { return { doSomething: ( arguments ) => { // here you can dispatch and use your arguments } }; } 是将提供给连接组件的道具。

所有mapDispatchToProps函数都格式不正确。

SIDE NOTE / OPINION - TLDR

将来如果您要发布大量代码,我相信如果这些代码链接在一起会更容易消化。

doSomething

组件按顺序显示:header - &gt;身体 - &gt;页脚。按顺序为它们提供代码,并将它们的动作,reducer,presentational和容器信息放在一个块中。

标题

// App.js
const App = () => (
    <div>
        <Header />
        <Body />
        <Footer />
    </div>
);

身体......

页脚......

我不知道您的代码是否有所不同,但您的// header.presentational.js ... // header.container.js ... ( or where you mapStateToProps and connect ) // header.actions.js ... // header.reducer.js ... 功能仍未正确格式化。

改变这个......

mapStateToDispatch

对此:

const mapDispatchToProps = dispatch => ({
  handleEditInvoice: invoice => {
    dispatch(showInvoiceEditPopover(invoice))
  }
})

答案 1 :(得分:0)

问题出在containers / ControlPopover.js和mapStateToProps函数中。需要将isActive属性分配给state.popover.isActive

相关问题