1
0
mirror of https://github.com/ascribe/onion.git synced 2025-02-14 21:10:27 +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() { reset() {
// If onReset prop is defined from outside, // If onReset prop is defined from outside,
// notify component that a form reset is happening. // notify component that a form reset is happening.
@ -69,9 +74,9 @@ let Form = React.createClass({
this.props.onReset(); this.props.onReset();
} }
for(let ref in this.refs) { for(let ref in this._refs) {
if(typeof this.refs[ref].reset === 'function') { if(typeof this._refs[ref].reset === 'function') {
this.refs[ref].reset(); this._refs[ref].reset();
} }
} }
this.setState(this.getInitialState()); this.setState(this.getInitialState());
@ -124,8 +129,8 @@ let Form = React.createClass({
getFormData() { getFormData() {
let data = {}; let data = {};
for (let refName in this.refs) { for (let refName in this._refs) {
const ref = this.refs[refName]; const ref = this._refs[refName];
if (ref.state && 'value' in ref.state) { if (ref.state && 'value' in ref.state) {
// An input can also provide an `Object` as a value // An input can also provide an `Object` as a value
@ -154,9 +159,9 @@ let Form = React.createClass({
this.props.handleSuccess(response); this.props.handleSuccess(response);
} }
for(let ref in this.refs) { for(let ref in this._refs) {
if(this.refs[ref] && typeof this.refs[ref].handleSuccess === 'function'){ if(this._refs[ref] && typeof this._refs[ref].handleSuccess === 'function'){
this.refs[ref].handleSuccess(response); this._refs[ref].handleSuccess(response);
} }
} }
this.setState({ this.setState({
@ -168,8 +173,8 @@ let Form = React.createClass({
handleError(err) { handleError(err) {
if (err.json) { if (err.json) {
for (let input in err.json.errors){ for (let input in err.json.errors){
if (this.refs && this.refs[input] && this.refs[input].state) { if (this._refs && this._refs[input] && this._refs[input].state) {
this.refs[input].setErrors(err.json.errors[input]); this._refs[input].setErrors(err.json.errors[input]);
} else { } else {
this.setState({errors: this.state.errors.concat(err.json.errors[input])}); this.setState({errors: this.state.errors.concat(err.json.errors[input])});
} }
@ -196,9 +201,9 @@ let Form = React.createClass({
}, },
clearErrors() { clearErrors() {
for(let ref in this.refs){ for(let ref in this._refs){
if (this.refs[ref] && typeof this.refs[ref].clearErrors === 'function'){ if (this._refs[ref] && typeof this._refs[ref].clearErrors === 'function'){
this.refs[ref].clearErrors(); this._refs[ref].clearErrors();
} }
} }
this.setState({errors: []}); this.setState({errors: []});
@ -247,17 +252,19 @@ let Form = React.createClass({
renderChildren() { renderChildren() {
return ReactAddons.Children.map(this.props.children, (child, i) => { return ReactAddons.Children.map(this.props.children, (child, i) => {
if (child) { 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, { return React.cloneElement(child, {
handleChange: this.handleChangeChild, 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, key: i,
// We need this in order to make editable be overridable when setting it directly // We need this in order to make editable be overridable when setting it directly
// on Property // on Property
@ -338,10 +345,10 @@ let Form = React.createClass({
const validatedFormInputs = {}; const validatedFormInputs = {};
Object Object
.keys(this.refs) .keys(this._refs)
.forEach((refName) => { .forEach((refName) => {
let refToValidate = {}; let refToValidate = {};
const property = this.refs[refName]; const property = this._refs[refName];
const input = property.refs.input; const input = property.refs.input;
const value = input.getDOMNode().value || input.state.value; const value = input.getDOMNode().value || input.state.value;
const { max, 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 * This method is overloaded so that we can track the ready-state
* of each uploader in the component * of each uploader in the component
@ -88,7 +93,7 @@ let RegisterPieceForm = React.createClass({
handleChangedThumbnail(thumbnailFile) { handleChangedThumbnail(thumbnailFile) {
const { digitalWorkFile } = this.state; const { digitalWorkFile } = this.state;
const { fineuploader } = this.refs.digitalWorkFineUploader.refs; const { fineuploader } = this._refs.digitalWorkFineUploader.refs;
fineuploader.setThumbnailForFileId( fineuploader.setThumbnailForFileId(
digitalWorkFile.id, digitalWorkFile.id,
@ -160,7 +165,7 @@ let RegisterPieceForm = React.createClass({
ignoreFocus={true} ignoreFocus={true}
label={getLangText('Your Work')}> label={getLangText('Your Work')}>
<InputFineUploader <InputFineUploader
ref={ref => this.refs.digitalWorkFineUploader = ref} ref={ref => this._refs.digitalWorkFineUploader = ref}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: AppConstants.serverUrl + 's3/key/',
fileClass: 'digitalwork' fileClass: 'digitalwork'
@ -182,7 +187,7 @@ let RegisterPieceForm = React.createClass({
name="thumbnail_file" name="thumbnail_file"
expanded={this.isThumbnailDialogExpanded()}> expanded={this.isThumbnailDialogExpanded()}>
<InputFineUploader <InputFineUploader
ref={ref => this.refs.thumbnailFineUploader = ref} ref={ref => this._refs.thumbnailFineUploader = ref}
fileInputElement={UploadButton({ className: 'btn btn-secondary btn-sm' })} fileInputElement={UploadButton({ className: 'btn btn-secondary btn-sm' })}
createBlobRoutine={{ createBlobRoutine={{
url: ApiUrls.blob_thumbnails 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() { componentDidMount() {
if(this.props.autoFocus) { if(this.props.autoFocus) {
this.handleFocus(); this.handleFocus();
@ -84,7 +89,7 @@ const Property = React.createClass({
}, },
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
let childInput = this.refs.input; let childInput = this._refs.input;
// For expanded there are actually three use cases: // For expanded there are actually three use cases:
// //
@ -124,7 +129,7 @@ const Property = React.createClass({
}, },
reset() { reset() {
let input = this.refs.input; let input = this._refs.input;
// maybe do reset by reload instead of front end state? // maybe do reset by reload instead of front end state?
this.setState({value: this.state.initialValue}); this.setState({value: this.state.initialValue});
@ -179,11 +184,11 @@ const Property = React.createClass({
} }
// skip the focus of non-input elements // skip the focus of non-input elements
let nonInputHTMLElements = ['pre', 'div']; let nonInputHTMLElements = ['pre', 'div'];
if (this.refs.input && if (this._refs.input &&
nonInputHTMLElements.indexOf(this.refs.input.getDOMNode().nodeName.toLowerCase()) > -1 ) { nonInputHTMLElements.indexOf(this._refs.input.getDOMNode().nodeName.toLowerCase()) > -1 ) {
return; return;
} }
this.refs.input.getDOMNode().focus(); this._refs.input.getDOMNode().focus();
this.setState({ this.setState({
isFocused: true isFocused: true
}); });
@ -205,7 +210,7 @@ const Property = React.createClass({
errors: null, errors: null,
// also update initialValue in case of the user updating and canceling its actions again // 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 the component is actually being shown (!== 'expanded === false')
if((this.state.expanded && this.props.checkboxLabel) || !this.props.checkboxLabel) { if((this.state.expanded && this.props.checkboxLabel) || !this.props.checkboxLabel) {
return ReactAddons.Children.map(this.props.children, (child) => { return ReactAddons.Children.map(this.props.children, (child) => {
// Since refs will be overriden by this functions return statement, const childWithProps = React.cloneElement(child, {
// 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, {
style, style,
onChange: this.handleChange, onChange: this.handleChange,
onFocus: this.handleFocus, onFocus: this.handleFocus,
onBlur: this.handleBlur, onBlur: this.handleBlur,
setWarning: this.setWarning, setWarning: this.setWarning,
disabled: !this.props.editable, 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, name: this.props.name,
setExpanded: this.setExpanded setExpanded: this.setExpanded
}); });
return childWithProps;
}); });
} }
}, },