Skip to content

Instantly share code, notes, and snippets.

@totty90
Created February 20, 2014 12:39
Show Gist options
  • Save totty90/9112611 to your computer and use it in GitHub Desktop.
Save totty90/9112611 to your computer and use it in GitHub Desktop.
React.js bug? getInitialState called more than once in a component lifecycle
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;
} );
'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);
},
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;
} );
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