您好,我是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
containers/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
containers/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
containers/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
components/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/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
我知道这是大量的示例代码,所以我尽量保持简短和专注,同时给出应用程序的全面视图。任何帮助都是非常感谢的。
发布于 2017-07-23 18:08:20
我认为您的问题是您的mapDispatchToProp
函数格式不正确。
您需要返回一个具有方法的对象。这些方法将作为道具提供给已连接的组件。
示例:
const mapDispatchToProps = ( dispatch ) => {
return {
doSomething: ( arguments ) => {
// here you can dispatch and use your arguments
}
};
}
doSomething
是将被提供给连接的组件的支柱。
所有mapDispatchToProps函数的格式都不正确。
旁注/观点- TLDR
在未来,如果你有很多代码要发布,我相信如果这些片段链接在一起,会更容易消化。
也就是说。
// App.js
const App = () => (
<div>
<Header />
<Body />
<Footer />
</div>
);
组件按以下顺序出现: header -> body -> footer。按此顺序提供它们的代码,并在一个块中包含它们的操作、缩减程序、表示形式和容器信息。
标题
// 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))
}
})
要这样做:
const mapDispatchToProps = dispatch => ({
return {
handleEditInvoice: invoice => {
dispatch(showInvoiceEditPopover(invoice))
}
};
})
发布于 2017-07-23 19:33:55
问题出在containers/ControlPopover.js和mapStateToProps
函数中。需要将isActive
属性分配给state.popover.isActive
https://stackoverflow.com/questions/45268062
复制相似问题