我正在制作一个paginate组件,我在传递给这个paginate组件的道具上遇到了问题。元素道具正在自动更新,所以我不能使用componentWillReceiveProps来更新我的组件。
下面是我的父组件呈现和对Paginate组件的回调:
class AlarmManagement extends React.Component{
constructor(props){
super(props)
this.state = {
alarms: undefined,
nameIsOrdered: false,
codeIsOrdered: true,
priorityIsOrdered: false,
alarmsPaginate: undefined,
}
this.orderByName = this.orderByName.bind(this)
this.orderByCode = this.orderByCode.bind(this)
this.orderByPriority = this.orderByPriority.bind(this)
this.getAlarms = this.getAlarms.bind(this)
this.getAlarmsPaginate = this.getAlarmsPaginate.bind(this)
}
componentWillMount(){
this.getAlarms()
}
getAlarms(){
$.ajax({
type: 'GET',
url: '/api/alarm-event-types/',
headers: { 'Authorization': "Token " + localStorage.token },
success: (alarms) => {
this.setState({
alarms: alarms.sort((a, b) => a.code - b.code)
})
},
error: (fail) => console.log(fail)
})
}
getAlarmsPaginate(page, amount) {
const newAlarms = this.state.alarms
this.setState({ alarmsPaginate: newAlarms.slice(page, amount) });
}
componentWillReceiveProps({emergency}){
if(emergency !== undefined && emergency !== null){
let updateList = [],
shouldUpdate = false
emergency.forEach((el, index) => {
if(el.update){
updateList.push(index)
}
if(el.update == "alarm"){
shouldUpdate = true
}
})
if(shouldUpdate){
this.getAlarms()
}
updateList.forEach((el) => {
if(this.props.clearList){
this.props.clearList(el)
}
})
}
}
orderByName(){
let alarms = this.state.alarms
if(this.state.nameIsOrdered === true){
this.setState({
alarms: alarms.sort((a, b) => {
let nameA = `${a.name.toUpperCase()}`,
nameB = `${b.name.toUpperCase()}`
if (nameA > nameB) {
return 1;
}
if (nameA < nameB) {
return -1;
}
return 0;
}),
nameIsOrdered: false,
codeIsOrdered: false,
priorityIsOrdered: false
})
}
else{
this.setState({
alarms: alarms.sort((a, b) => {
let nameA = `${a.name.toUpperCase()}`,
nameB = `${b.name.toUpperCase()}`
if (nameB > nameA) {
return 1;
}
if (nameB < nameA) {
return -1;
}
return 0;
}),
nameIsOrdered: true,
codeIsOrdered: false,
priorityIsOrdered: false
})
}
}
orderByCode(){
let alarms = this.state.alarms
if(this.state.codeIsOrdered === true){
this.setState({
alarms: alarms.sort((a, b) => b.code - a.code),
codeIsOrdered: false,
nameIsOrdered: false,
priorityIsOrdered: false
})
}
else{
this.setState({
alarms: alarms.sort((a, b) => a.code - b.code),
codeIsOrdered: true,
nameIsOrdered: false,
priorityIsOrdered: false
})
}
}
orderByPriority(){
let alarms = this.state.alarms
if(this.state.priorityIsOrdered === true){
this.setState({
alarms: alarms.sort((a, b) => b.priority - a.priority),
nameIsOrdered: false,
codeIsOrdered: false
})
}
else{
this.setState({
alarms: alarms.sort((a, b) => a.priority - b.priority),
nameIsOrdered: false,
codeIsOrdered: false
})
}
this.setState(prevState => ({
priorityIsOrdered: !prevState.priorityIsOrdered,
}))
}
render(){
const alarms = this.state.alarms,
alarmsPaginate = this.state.alarmsPaginate;
return(
this.props.user.groups == 1 ? (
<div className="contactto-middle-content">
<ManagementMenu/>
<div className="management-content">
<div className="list-management-subtitle">ALARMES</div>
<button type="button" className="btn btn--save-attend-or-add-someone btn--color-tecno" onClick={() => browserHistory.push('/app/alarms/form/add')}>
<div><span className="btn--bold">+ ADICIONAR</span> ALARME</div>
</button>
{alarms &&
<div className="list-table">
<Paginate outerClass="paginate-wrapper" filterElements={this.getAlarmsPaginate} maxElements={5} elements={alarms} />
<div className="list-table-header">
<div className="left list-table--200"><span className="icons-order clickable" onClick={this.orderByName}>Nome<Order width="15" height="10"/></span></div>
<div className="left list-table--200"><span className="icons-order clickable" onClick={this.orderByCode}>Código<Order width="15" height="10"/></span></div>
<div className="left list-table--200"><span className="icons-order clickable" onClick={this.orderByPriority}>Prioridade<Order width="15" height="10"/></span></div>
<div className="list-table-body-column--action--2icons"></div>
</div>
<div className="list-table-body scroll">
{alarmsPaginate && alarmsPaginate.map((alarm) =>
<AlarmRow key={alarm.code} alarm={alarm} getAlarms={this.getAlarms} channelId={this.props.channelId} iconDetail={this.refs["iconDetail"]}/>
)}
</div>
</div>
}
</div>
</div>
):
<div className="error">Página não pode ser acessada, pois você não é um administrador</div>
)
}
}
export default AlarmManagement
下面是我的分页组件:
export default class Paginate extends React.Component{
constructor(props){
super(props)
this.state = {
pagesNumber: undefined,
elements: undefined,
elementsNumber: undefined,
startNumber: undefined,
endNumber: undefined,
pos: undefined,
}
this.nextList = this.nextList.bind(this)
this.previousList = this.previousList.bind(this)
}
previousList(){
if(this.state.pos > 1){
let pos = this.state.pos - 1
this.changePage(pos)
}
}
nextList(){
if(this.state.pos < this.state.pagesNumber){
let pos = this.state.pos + 1
this.changePage(pos)
}
}
changePage(pos){
const newStartNumber = pos === 1 ? 0 : this.props.maxElements * (pos - 1);
const newEndNumber = this.props.maxElements * pos > this.state.elementsNumber ? this.state.elementsNumber : this.props.maxElements * pos;
this.props.filterElements(newStartNumber, newEndNumber);
this.setState({
pos: pos,
startNumber: newStartNumber,
endNumber: newEndNumber,
});
}
componentWillReceiveProps(nextProps){
console.log(nextProps.elements != this.props.elements ? 'different' : 'equal')
}
componentWillMount(){
this.setState((prevState, props) => ({
pagesNumber: props.elements ? Math.ceil(props.elements.length / props.maxElements) : 0,
elements: props.elements,
elementsNumber: props.elements.length,
startNumber: 1,
endNumber: props.maxElements,
pos: 1,
}));
if(this.props.filterElements){
this.props.filterElements(0, this.props.maxElements)
}
}
render(){
const elementsNumber = this.state.elementsNumber,
startNumber = this.state.startNumber,
endNumber = this.state.endNumber;
return(
<div className={this.props.outerClass}>
{elementsNumber > this.props.maxElements &&
<div className="left contactto-100">
{elementsNumber && <span className="left">{`${startNumber === 0 ? 1 : startNumber}-${endNumber} de ${elementsNumber}`}</span>}
<span className="paginate-arrow paginate-arrow-left" onClick={this.previousList}></span>
<span className="paginate-arrow paginate-arrow-right" onClick={this.nextList}></span>
</div>
}
</div>
)
}
}
Paginate.defaultProps = {
outerClass: 'paginate-wrapper'
}
Paginate.propTypes = {
outerClass: React.PropTypes.string,
filterElements: React.PropTypes.func.isRequired,
maxElements: React.PropTypes.number.isRequired,
elements: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
}
代码在post advisions之后更新,但是:componentWillReceiveProps(nextProps){console.log(nextProps.elements!=this.props.elements?'difference':'equal')//still are equal}
怎么啦?
提前谢了。
您的问题是什么,或者您的代码打算如何运行,这并不十分清楚。但是,您在代码中做了一些非常不明智的事情...
changePage(pos){
this.state.startNumber = pos === 1 ? 0 : this.props.maxElements * (pos - 1);
this.state.endNumber = this.props.maxElements * pos > this.state.elementsNumber ? this.state.elementsNumber : this.props.maxElements * pos;
this.props.filterElements(this.state.elements, this.state.startNumber, this.state.endNumber);
this.setState({
pos: pos,
startNumber: this.state.startNumber,
endNumber: this.state.endNumber
});
}
虽然在函数末尾正确地使用setState()更新状态,但在此之前直接更改状态。您可以通过使用临时变量来保存新状态,或者在setState调用中进行计算来解决此问题。例如。
changePage(pos){
const newStartNumber = pos === 1 ? 0 : this.props.maxElements * (pos - 1);
const newEndNumber = this.props.maxElements * pos > this.state.elementsNumber ? this.state.elementsNumber : this.props.maxElements * pos;
this.props.filterElements(this.state.elements, newStartNumber, newEndNumber);
this.setState({
pos: pos,
startNumber: newStartNumber,
endNumber: newEndNumber
});
}
在构造函数或componentWillReceiveProps()方法中使用props来设置状态也是一种反模式。通常,在React应用程序中,我们希望有一个单一的真值源--也就是说,所有数据都由一个单一组件负责,而且只有一个组件。该组件负责在其状态中存储数据,并通过道具将数据分发到其他组件。
constructor(props){
super(props)
this.state = {
elements: this.props.elements,
...
}
...
}
在您的特定情况下,父组件有责任在其状态中维护元素。子组件接收作为道具的元素--它不应该以任何方式将它们存储为状态或直接更新元素。如果子操作需要更新元素,则需要通过从父操作作为道具传递给它的函数来完成。
我在上面作为示例使用的changePage函数可以变成...
changePage(pos){
const newStartNumber = pos === 1 ? 0 : this.props.maxElements * (pos - 1);
const newEndNumber = this.props.maxElements * pos > this.props.elements.length ? this.props.elements.length : this.props.maxElements * pos;
this.props.filterElements(this.props.elements, newStartNumber, newEndNumber);
this.setState({
pos: pos,
startNumber: newStartNumber,
endNumber: newEndNumber
});
}
但是,我们可以更进一步--为什么我们需要将这个。props.elements传递给filterElements函数?父组件必须已经具有对元素的引用;毕竟它一开始就给了我们!
changePage(pos){
const newStartNumber = pos === 1 ? 0 : this.props.maxElements * (pos - 1);
const newEndNumber = this.props.maxElements * pos > this.props.elements.length ? this.props.elements.length : this.props.maxElements * pos;
this.props.filterElements(newStartNumber, newEndNumber);
this.setState({
pos: pos,
startNumber: newStartNumber,
endNumber: newEndNumber
});
}
getAlarmsPaginate(alarms, page, amount) {
this.state.alarmsPaginate = alarms.slice(page, amount)
this.setState({ alarmsPaginate: this.state.alarmsPaginate })
}
变成...
getAlarmsPaginate(page, amount) {
const newAlarmsPaginate = this.state.alarms.slice(page, amount);
this.setState({ alarmsPaginate: newAlarmsPaginate });
}
注意,我们直接对this.state.alarms进行切片,而不是一个函数参数--我们也不再改变我们的状态--而是专门使用setState函数。
在更多的情况下,您在整个代码中不恰当地使用了道具和状态--我会仔细检查并确保您遵循了上面列出的指导方针--或者更好地阅读React文档。当您遵循这些实践时,您很可能会发现您的问题解决了,但如果没有,请将修改后的代码发回到这里,我将很高兴进一步提供帮助。
class Parent extends React.Component {
constructor() {
super();
// our parent maintains an array of all elements in its state,
// as well as the current page, and the number of items per page.
// getSampleData can be seen in the fiddle - it just makes an array
// of objects for us to render
this.state = {
allElements: getSampleData(),
page: 1,
numPerPage: 10
};
}
// we pass this function as a prop to our Paginate component to allow
// it to update the state of Parent
setPage(pageNum) {
this.setState({
page: pageNum
});
}
render() {
// get the appropriate elements from our own state
const firstItem = (this.state.page - 1) * this.state.numPerPage;
const lastItem = this.state.page * this.state.numPerPage;
const elementRender =
this.state.allElements
.slice(firstItem, lastItem)
.map(element => {
return (
<div key={element.itemNumber}>{element.itemName}</div>
);
});
// numberOfElements, numPerPage and the setPage function from
// Parent's state are passed
// to the paginate component as props
return (
<div>
<Paginate
numberOfElements={this.state.allElements.length}
numPerPage={this.state.numPerPage}
setPage={this.setPage.bind(this)}
/>
{elementRender}
</div>
);
}
}
class Paginate extends React.Component {
render() {
const numberOfButtons = Math.ceil(this.props.numberOfElements / this.props.numPerPage);
const buttons = [];
// make a first page button
buttons.push(<button key={0} onClick={() => this.props.setPage(1)}>First Page</button>);
let i = 0;
// add a button for each page we need
while (i < numberOfButtons) {
const page = ++i;
buttons.push(
<button
key={i}
onClick={() => this.props.setPage(page)}
>
Page {i}
</button>
);
}
// add a last page button
buttons.push(<button key={i+1} onClick={() => this.props.setPage(i)}>Last Page</button>);
return (
<div>
{buttons}
</div>
);
}
}
问题内容: 我在使用React表单和正确管理状态时遇到麻烦。我有一个形式为模式的时间输入字段。初始值在中设置为状态变量,并从父组件传递。这本身工作正常。 当我想通过父组件更新默认的start_time值时,就会出现问题。更新本身通过发生在父组件中。但是在我的表单中,默认的start_time值从不更改,因为它仅在中定义一次。 我尝试过通过强制进行状态更改,该更改确实有效,但给了我错误。 所以我的问
我正在做一个React-Native项目,我意识到React-Native似乎打破了React-flow(父到子)道具更新。 基本上,我是从“应用程序”组件调用“菜单”组件,将一个道具传递给“菜单”。然而,当我更新“应用程序”状态时,“菜单”上的道具应该更新,但这不会发生。我做错什么了吗? 这是我的密码: 一个pp.js 菜单js
问题内容: 什么是对React组件prop更新进行单元测试的正确方法。 这是我的测试装置; 这可以正常工作,并且测试通过了,但是显示了反应警告消息 我要测试的只是属性的更新,而不是使用其他属性创建元素的新实例。有没有更好的方法来执行此属性更新? 问题答案: AirBnB的酶库为这个问题提供了一个优雅的解决方案。 它提供了一个setProps方法,可以在浅包装或jsdom包装上调用该方法。
我有问题的反应形式和管理的状态适当。我在一个表单中有一个时间输入字段(在一个模态中)。初始值在中设置为状态变量,并从父组件传入。这本身就很好用。 当我想通过父组件更新默认的start_time值时,问题就来了。更新本身通过在父组件中发生。但是,在我的表单中,默认的start_time值从不更改,因为它只在中定义过一次。 我尝试使用通过强制更改状态,这确实起到了作用,但给了我错误。 所以我的问题是,
在简单的待办事项列表应用程序中,我使用了组件,它为组件提供了道具: 问题是,该组件在每次状态更改时都会呈现 例如,切换todo项将强制呈现
问题内容: 我正在尝试将表示性组件与容器组件分开。我有一个和一个。容器负责触发redux操作,以根据当前用户获取适当的网站。 问题是在最初呈现容器组件之后,异步获取了当前用户。这意味着容器组件不知道它需要重新执行其函数中的代码,该代码将更新数据以发送到。我认为我需要在其props(user)之一更改时重新渲染容器组件。如何正确执行此操作? 问题答案: 您必须在方法中添加条件。 该示例用于比较对象。