Created
February 20, 2014 12:39
-
-
Save totty90/9112611 to your computer and use it in GitHub Desktop.
React.js bug? getInitialState called more than once in a component lifecycle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define([ | |
"react", | |
'future', | |
'API', | |
'../_helpers/index', | |
'../_helpers/ItemRenderer', | |
'./SelectArticleClassesStep', | |
'./SelectOperationClassesStep', | |
], function( | |
React, | |
Future, | |
API, | |
helpers, | |
ItemRenderer, | |
SelectArticleClassesStep, | |
SelectOperationClassesStep | |
){ | |
"use strict"; | |
var ReactC = React.createClass({ | |
getInitialState: function(){ | |
this.__steps = [ | |
new SelectArticleClassesStep({ | |
router : this.props.router, | |
onNext : this.__goToNextStep, | |
}), | |
new SelectOperationClassesStep({ | |
router : this.props.router, | |
onNext : this.__goToNextStep, | |
}) | |
] | |
console.log('creted steps', this.__steps) | |
return { | |
// Steps: | |
// 0: select article classes | |
// 1: select operation classes | |
// 2: select workers | |
step : this.props.step || 0, | |
}; | |
}, | |
__goToNextStep: function(){ | |
var nextStep = this.state.step + 1; | |
this.props.router.navigate('job/add/step:'+nextStep, {trigger: false}); | |
this.setState({ | |
step: nextStep | |
}) | |
}, | |
getStepStates: function(){ | |
return _.map(this.__steps, function(step){ | |
return $.extend({}, step.state); | |
}) | |
}, | |
componentWillMount: function(){ | |
}, | |
componentWillUnmount: function(){ | |
if(this.props.onUnmount){ | |
this.props.onUnmount(this) | |
} | |
}, | |
setStepState: function(stepNumber, stepState){ | |
if(this.__steps[stepNumber].isMounted()){ | |
this.__steps[stepNumber].setState(stepState) | |
}else{ | |
debugger | |
} | |
}, | |
__getCurrentView: function(){ | |
return this.__steps[this.state.step] | |
}, | |
render: function(){ | |
return ( | |
React.DOM.div({ | |
ref: 'root', | |
className: 'root-view' | |
}, [ | |
this.__getCurrentView() | |
]) | |
) | |
} | |
}) | |
return ReactC; | |
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'job/add': function(step){ | |
step = Number(step); | |
if(step === 0){ | |
var view = arguments[1]; | |
var articleId = arguments[2]; | |
} | |
if(singleton.job.add.view){ | |
singleton.job.add.view.setState({ | |
step : Number(step), | |
}) | |
if(step === 0){ | |
singleton.job.add.view.setStepState(0, { | |
viewType : view, | |
selectedArticleId : articleId, | |
}) | |
} | |
return; | |
} | |
var c = new views.job.Add({ | |
router : this, | |
step : Number(step) | |
}); | |
singleton.job.add.view = c; | |
setView(c); | |
}, |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define([ | |
"react", | |
'future', | |
'API', | |
'../_helpers/index', | |
'../_helpers/ItemRenderer', | |
'./SetArticleDetails' | |
], function( | |
React, | |
Future, | |
API, | |
helpers, | |
ItemRenderer, | |
SetArticleDetails | |
){ | |
"use strict"; | |
// The component has 2 views: | |
// - selectArticle: in this view a list of articles are shown. When the | |
// the user clicks on an article will go to the addDetails | |
// view. | |
// - addDetails : is rendered the SetArticleDetails component. | |
var ReactC = React.createClass({ | |
getInitialState: function(){ | |
return $.extend({ | |
// selectArticle or addDetails | |
viewType: 'selectArticle', | |
// When provided from the parent. | |
selectedArticleId: this.props.selectedArticleId, | |
// Contains the articles as they came from the database. | |
articleClasses: [], | |
// Contains supplemental data about the articles, such as | |
// quantity and serial numbers. | |
// articleClassesTemporaryData: {}, | |
}, this.props.lastState); | |
}, | |
componentWillMount: function(){ | |
API('getArticleClasses').then(function(response){ | |
if(response.err){ | |
return alert(response.err) | |
} | |
this.setState({articleClasses: response.data}); | |
}.bind(this)) | |
}, | |
__onItemRendererClick: function(articleClass){ | |
this.setState({ | |
selectedArticleId : articleClass._id, | |
viewType : 'addDetails', | |
}, function(){ | |
this.__updateUrl() | |
}) | |
}, | |
// Data format: | |
// { | |
// quantity : Number, | |
// serialNumbers : [String, String, ...], | |
// articleClass : ArticleClass | |
// } | |
__onArticleDetailsSave: function(data){ | |
var articleClassesTemporaryData = $.extend({}, this.state.articleClassesTemporaryData); | |
articleClassesTemporaryData[data.articleClass._id] = { | |
quantity : data.quantity, | |
serialNumbers : data.serialNumbers, | |
} | |
this.setState({ | |
articleClassesTemporaryData : articleClassesTemporaryData, | |
selectedArticleId : null, | |
viewType : 'selectArticle' | |
}, function(){ | |
this.__updateUrl() | |
}.bind(this)); | |
}, | |
__updateUrl: function(){ | |
var nextUrl = 'job/add/step:0/view:' + this.state.viewType; | |
if(this.state.viewType === 'addDetails'){ | |
nextUrl += '/articleId:' + this.state.selectedArticleId | |
} | |
this.props.router.navigate(nextUrl, {trigger: false}); | |
}, | |
__getCurrentView: function(){ | |
var articleClassesTemporaryData = this.state.articleClassesTemporaryData || {} | |
if(this.state.viewType === 'selectArticle'){ | |
return [ | |
React.DOM.h1({}, 'Seleccionar os artigos'), | |
_.map(this.state.articleClasses, function(articleClass){ | |
var data = $.extend({}, articleClass, articleClassesTemporaryData[articleClass._id]); | |
if(data.serialNumbers){ | |
data.serialNumbers = data.serialNumbers.join(', '); | |
} | |
return new ItemRenderer({ | |
data : data, | |
visibleKeys : 'code description quantity serialNumbers'.split(' '), | |
onClick : this.__onItemRendererClick, | |
selected : data.quantity > 0 | |
}) | |
}.bind(this)), | |
helpers.createButton({ | |
title: 'Próximo', | |
onClick: this.props.onNext, | |
}) | |
] | |
}else if(this.state.viewType === 'addDetails'){ | |
// Override the default data with the one stored in article data. | |
var view = new SetArticleDetails($.extend({ | |
articleClass : this.__getArticleClassById(this.state.selectedArticleId), | |
quantity : 1, | |
onSave : this.__onArticleDetailsSave, | |
serialNumbers: [], | |
}, articleClassesTemporaryData[this.state.selectedArticleId])); | |
return view; | |
} | |
}, | |
__getArticleClassById: function(articleId){ | |
var a = this.state.articleClasses; | |
if(!a){return false;} | |
var l = a.length; | |
while(l--){ | |
if(a[l]._id === articleId){ | |
return a[l]; | |
} | |
} | |
}, | |
render: function(){ | |
return ( | |
React.DOM.div({ | |
ref: 'root', | |
className: 'root-view' | |
}, [ | |
this.__getCurrentView() | |
]) | |
) | |
} | |
}) | |
return ReactC; | |
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define([ | |
"react", | |
'future', | |
'API', | |
'../_helpers/index', | |
'../_helpers/ItemRenderer', | |
], function( | |
React, | |
Future, | |
API, | |
helpers, | |
ItemRenderer | |
){ | |
"use strict"; | |
var ReactC = React.createClass({ | |
getInitialState: function(){ | |
return { | |
quantity : this.props.quantity, | |
// Array | |
serialNumbers : this.props.serialNumbers, | |
}; | |
}, | |
getDefaultProps: function(){ | |
return { | |
articleClass : null, | |
quantity : 0, | |
serialNumbers : [], | |
onSave : null, | |
} | |
}, | |
__onQuantityChange: function(evt){ | |
this.setState({ | |
quantity: Number(evt.target.value) | |
}) | |
}, | |
__onSerialNumberChange: function(serialNumberIndex, evt){ | |
var serialNumbers = this.state.serialNumbers.slice(); | |
serialNumbers[serialNumberIndex] = evt.target.value; | |
this.setState({serialNumbers: serialNumbers}) | |
}, | |
__save: function(){ | |
var serialNumbers = this.state.serialNumbers.slice(0, this.state.quantity); | |
this.setState({serialNumbers: serialNumbers}); | |
if(this.props.onSave){ | |
this.props.onSave({ | |
// The state's serialNumbers are not already updated to the | |
// latest version. Can lead to bugs if you add quantity 2 | |
// then insert the serial numbers, set quantity 1. You will receive | |
// quantity = 1 and 2 serial numbers. | |
// Don't do: this.state.serialNumbers | |
serialNumbers : serialNumbers, | |
quantity : this.state.quantity, | |
articleClass : this.props.articleClass, | |
}); | |
} | |
}, | |
render: function(){ | |
return ( | |
React.DOM.div({ | |
ref: 'root', | |
className: 'root-view' | |
}, [ | |
React.DOM.h1({}, 'Adicionar detalhes do artigo'), | |
React.DOM.form({}, [ | |
helpers.createInput('Quantidade', 'quantity', null, this.__onQuantityChange.bind(this), this.state.quantity), | |
_.times(this.state.quantity, function(i){ | |
return helpers.createInput("Numero de serie: ", 'serialNumber' + i, null, this.__onSerialNumberChange.bind(this, i), this.state.serialNumbers[i]); | |
}, this), | |
helpers.createButton({ | |
title : 'Gravar', | |
onClick : this.__save | |
}) | |
]) | |
]) | |
) | |
} | |
}) | |
return ReactC; | |
} ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment