1
0
mirror of https://github.com/ascribe/onion.git synced 2025-01-26 01:32:16 +01:00

Fix callback ref propagation to parent components

This commit is contained in:
Brett Sun 2016-02-12 10:33:20 +01:00
parent dcdae7b7b1
commit 65f7d02e6d
3 changed files with 64 additions and 43 deletions

View File

@ -62,6 +62,11 @@ let Form = React.createClass({
};
},
componentWillMount() {
// Set up internal storage for callback refs
this._refs = {};
},
reset() {
// If onReset prop is defined from outside,
// notify component that a form reset is happening.
@ -69,9 +74,9 @@ let Form = React.createClass({
this.props.onReset();
}
for(let ref in this.refs) {
if(typeof this.refs[ref].reset === 'function') {
this.refs[ref].reset();
for(let ref in this._refs) {
if(typeof this._refs[ref].reset === 'function') {
this._refs[ref].reset();
}
}
this.setState(this.getInitialState());
@ -124,8 +129,8 @@ let Form = React.createClass({
getFormData() {
let data = {};
for (let refName in this.refs) {
const ref = this.refs[refName];
for (let refName in this._refs) {
const ref = this._refs[refName];
if (ref.state && 'value' in ref.state) {
// An input can also provide an `Object` as a value
@ -154,9 +159,9 @@ let Form = React.createClass({
this.props.handleSuccess(response);
}
for(let ref in this.refs) {
if(this.refs[ref] && typeof this.refs[ref].handleSuccess === 'function'){
this.refs[ref].handleSuccess(response);
for(let ref in this._refs) {
if(this._refs[ref] && typeof this._refs[ref].handleSuccess === 'function'){
this._refs[ref].handleSuccess(response);
}
}
this.setState({
@ -168,8 +173,8 @@ let Form = React.createClass({
handleError(err) {
if (err.json) {
for (let input in err.json.errors){
if (this.refs && this.refs[input] && this.refs[input].state) {
this.refs[input].setErrors(err.json.errors[input]);
if (this._refs && this._refs[input] && this._refs[input].state) {
this._refs[input].setErrors(err.json.errors[input]);
} else {
this.setState({errors: this.state.errors.concat(err.json.errors[input])});
}
@ -196,9 +201,9 @@ let Form = React.createClass({
},
clearErrors() {
for(let ref in this.refs){
if (this.refs[ref] && typeof this.refs[ref].clearErrors === 'function'){
this.refs[ref].clearErrors();
for(let ref in this._refs){
if (this._refs[ref] && typeof this._refs[ref].clearErrors === 'function'){
this._refs[ref].clearErrors();
}
}
this.setState({errors: []});
@ -247,17 +252,19 @@ let Form = React.createClass({
renderChildren() {
return ReactAddons.Children.map(this.props.children, (child, i) => {
if (child) {
// Since refs will be overwritten by this functions return statement,
// we still want to be able to define refs for nested `Form` or `Property`
// children, which is why we're upfront simply invoking the callback-ref-
// function before overwriting it.
if(typeof child.ref === 'function' && this.refs[child.props.name]) {
child.ref(this.refs[child.props.name]);
}
return React.cloneElement(child, {
handleChange: this.handleChangeChild,
ref: child.props.name,
ref: (ref) => {
this._refs[child.props.name] = ref;
// Since refs will be overwritten by this functions return statement,
// we still want to be able to define refs for nested `Form` or `Property`
// children, which is why we're upfront simply invoking the callback-ref-
// function before overwriting it.
if (typeof child.ref === 'function') {
child.ref(ref);
}
},
key: i,
// We need this in order to make editable be overridable when setting it directly
// on Property
@ -338,10 +345,10 @@ let Form = React.createClass({
const validatedFormInputs = {};
Object
.keys(this.refs)
.keys(this._refs)
.forEach((refName) => {
let refToValidate = {};
const property = this.refs[refName];
const property = this._refs[refName];
const input = property.refs.input;
const value = input.getDOMNode().value || input.state.value;
const { max,

View File

@ -58,6 +58,11 @@ let RegisterPieceForm = React.createClass({
}
},
componentWillMount() {
// Set up internal storage for callback refs
this._refs = {};
},
/**
* This method is overloaded so that we can track the ready-state
* of each uploader in the component
@ -88,7 +93,7 @@ let RegisterPieceForm = React.createClass({
handleChangedThumbnail(thumbnailFile) {
const { digitalWorkFile } = this.state;
const { fineuploader } = this.refs.digitalWorkFineUploader.refs;
const { fineuploader } = this._refs.digitalWorkFineUploader.refs;
fineuploader.setThumbnailForFileId(
digitalWorkFile.id,
@ -160,7 +165,7 @@ let RegisterPieceForm = React.createClass({
ignoreFocus={true}
label={getLangText('Your Work')}>
<InputFineUploader
ref={ref => this.refs.digitalWorkFineUploader = ref}
ref={ref => this._refs.digitalWorkFineUploader = ref}
keyRoutine={{
url: AppConstants.serverUrl + 's3/key/',
fileClass: 'digitalwork'
@ -182,7 +187,7 @@ let RegisterPieceForm = React.createClass({
name="thumbnail_file"
expanded={this.isThumbnailDialogExpanded()}>
<InputFineUploader
ref={ref => this.refs.thumbnailFineUploader = ref}
ref={ref => this._refs.thumbnailFineUploader = ref}
fileInputElement={UploadButton({ className: 'btn btn-secondary btn-sm' })}
createBlobRoutine={{
url: ApiUrls.blob_thumbnails

View File

@ -77,6 +77,11 @@ const Property = React.createClass({
};
},
componentWillMount() {
// Set up internal storage for callback refs
this._refs = {};
},
componentDidMount() {
if(this.props.autoFocus) {
this.handleFocus();
@ -84,7 +89,7 @@ const Property = React.createClass({
},
componentWillReceiveProps(nextProps) {
let childInput = this.refs.input;
let childInput = this._refs.input;
// For expanded there are actually three use cases:
//
@ -124,7 +129,7 @@ const Property = React.createClass({
},
reset() {
let input = this.refs.input;
let input = this._refs.input;
// maybe do reset by reload instead of front end state?
this.setState({value: this.state.initialValue});
@ -179,11 +184,11 @@ const Property = React.createClass({
}
// skip the focus of non-input elements
let nonInputHTMLElements = ['pre', 'div'];
if (this.refs.input &&
nonInputHTMLElements.indexOf(this.refs.input.getDOMNode().nodeName.toLowerCase()) > -1 ) {
if (this._refs.input &&
nonInputHTMLElements.indexOf(this._refs.input.getDOMNode().nodeName.toLowerCase()) > -1 ) {
return;
}
this.refs.input.getDOMNode().focus();
this._refs.input.getDOMNode().focus();
this.setState({
isFocused: true
});
@ -205,7 +210,7 @@ const Property = React.createClass({
errors: null,
// also update initialValue in case of the user updating and canceling its actions again
initialValue: this.refs.input.getDOMNode().value
initialValue: this._refs.input.getDOMNode().value
});
},
@ -262,25 +267,29 @@ const Property = React.createClass({
// if the component is actually being shown (!== 'expanded === false')
if((this.state.expanded && this.props.checkboxLabel) || !this.props.checkboxLabel) {
return ReactAddons.Children.map(this.props.children, (child) => {
// Since refs will be overriden by this functions return statement,
// we still want to be able to define refs for nested `Form` or `Property`
// children, which is why we're upfront simply invoking the callback-ref-
// function before overriding it.
if(typeof child.ref === 'function' && this.refs.input) {
child.ref(this.refs.input);
}
return React.cloneElement(child, {
const childWithProps = React.cloneElement(child, {
style,
onChange: this.handleChange,
onFocus: this.handleFocus,
onBlur: this.handleBlur,
setWarning: this.setWarning,
disabled: !this.props.editable,
ref: 'input',
ref: (ref) => {
this._refs.input = ref;
// Since refs will be overriden by this functions return statement,
// we still want to be able to define refs for nested `Form` or `Property`
// children, which is why we're upfront simply invoking the callback-ref-
// function before overriding it.
if (typeof child.ref === 'function') {
child.ref(ref);
}
},
name: this.props.name,
setExpanded: this.setExpanded
});
return childWithProps;
});
}
},