React changing props on grandchildren: object is not extensible / tree traversing react children
I am following a tutorial and cannot get the following code to run.
When I run the following code I get the error Can't add property attachToForm, object is not extensible
. Are you no longer to allowed to change child props in this way (ie. with child.props.key = value)? If not can you see a better way of adding a function to nested children only if the element is an input?
React.Children.forEach(children, function (child) {
if (child.props.name) {
child.props.attachToForm = this.attachToForm;
child.props.detachFromForm = this.detachFromForm;
}
if (child.props.children) {
this.registerInputs(child.props.children);
}
}.bind(this));
I am using es6 if that changes anything but the tutorial can be found here: http://christianalfoni.github.io/javascript/2014/10/22/nailing-that-validation-with-reactjs.html
FormComponent:
'use strict';
import React from 'react';
import BaseComponent from '@client/base-component';
export default class Form extends BaseComponent {
constructor(props) {
super(props);
this.state = {};
}
render() {
var classString = this.props.className ? this.props.className : '';
var classArray = ['_common-form', this.props.type ? 'form--' + this.props.type : 'form--basic' ];
for(var i = 0; i < classArray.length; i++){
if(classArray[i]){
classString = classString + ' ' +classArray[i];
}
}
var props = {
type: this.props.type,
className: classString,
style: this.props.style,
onSubmit: this.props.onSubmit
};
return React.createElement(
'form',
props,
this.newChildren
);
}
componentWillMount() {
this.inputs = {};
this.model = {};
this.newChildren = [];
this.registerInputs(this.props.children);
}
registerInputs(children) {
React.Children.forEach(children, function (child) {
if (child.props.name) {
child.props.attachToForm = this.attachToForm;
child.props.detachFromForm = this.detachFromForm;
}
if (child.props.children) {
this.registerInputs(child.props.children);
}
}.bind(this));
}
attachToForm(component) {
this.inputs[component.props.name] = component;
this.model[component.props.name] = component.state.value;
}
detachFromForm(component) {
delete this.inputs[component.props.name];
delete this.model[component.props.name];
}
}
ModalComponent:
'use strict';
import React from 'react';
import Modal from '@client/common/modal';
import Button from '@client/common/buttons';
import AddParticipant from './add_participant';
import Form from '@client/common/form_elements';
import Input from '@client/common/form_elements/input.js';
import SlideToggle from '@client/common/form_elements/slide_toggle.js';
import FormField from '@client/common/form_elements/form_field.js';
import FormSection from '@client/common/form_elements/section.js';
import BaseComponent from '@client/base-component';
export default class EditTeam extends BaseComponent {
constructor(props) {
super(props);
this.state = {
values: {
name: props.team.name,
mission: props.team.mission,
globalSearch: props.team.globalSearch,
public: props.team.public,
witcryptSecured: props.team.witcryptSecured
},
addParticipantModal: false
};
}
render() {
var participantsList = [];
this.props.team.participants.forEach(function(participant) {
participantsList.push(
<div className="participant" key={participant.key}>
<span className="participant-avatar" style={{backgroundImage:`url("${participant.avatar}")`}}></span>
<span>{participant.name}</span>
<span className={`${participant.roll}-action roll`}>{participant.roll}<a></a></span>
</div>
);
}.bind(this));
return (
<Modal className="_common-edit-team-settings" title={`Edit ${this.props.team.name}`} isOpen={this.props.modalIsOpen && this.props.editTeamModal} onCancel={this.props.toggleEditTeamModal} backdropClosesModal>
<Form onSubmit={this.saveChanges}>
<FormSection className="edit-team-details" sectionHeader="Team Details">
<FormField label="Name">
<Input name="name" value={this.state.values.name} onChange={this.handleInputChange} type="text" placeholder={this.props.team.name}/>
</FormField>
<FormField label="Mission">
<Input name="mission" value={this.state.values.mission} onChange={this.handleInputChange} type="text" placeholder={this.props.team.kitMission || 'Kit Mission'} multiline />
</FormField>
</FormSection>
<FormSection className="privacy-settings" sectionHeader="Privacy Settings">
<FormField label="Included in global search results" >
<SlideToggle name="globalSearch" defaultChecked={this.state.values.globalSearch} onChange={this.handleCheckedChange} type="checkbox" />
</FormField>
<FormField label="Accessible by anyone" >
<SlideToggle name="public" defaultChecked={this.state.values.public} onChange={this.handleCheckedChange} type="checkbox" />
</FormField>
<FormField label="Secured with WitCrypt" >
<SlideToggle name="witcryptSecured" defaultChecked={this.state.values.witcryptSecured} onChange={this.handleCheckedChange} type="checkbox" />
</FormField>
</FormSection>
<FormSection sectionHeader="Participants">
{participantsList}
<div id="add-participant" className="participant" onClick={this.toggleAddParticipantModal}>
<span className="participant-avatar" style={{backgroundImage:'url(/img/blue_add.svg)'}}></span>
<span>Add a Participant</span>
<span className="add-action roll"><a></a></span>
</div>
</FormSection>
<Button type="hollow-primary" size="md" className="single-modal-btn" block submit>Save</Button>
</Form>
<AddParticipant people={this.props.people} toggleAddParticipantModal={this.props.toggleAddParticipantModal} modalIsOpen={this.props.modalIsOpen} toggleAddParticipantModal={this.toggleAddParticipantModal} addParticipantModal={this.state.addParticipantModal} />
</Modal>
);
}
toggleAddParticipantModal() {
this.setState({
addParticipantModal: !this.state.addParticipantModal
});
}
handleCheckedChange(event){
var newState = this.state;
newState.values[event.target.name] = !newState.values[event.target.name];
this.setState(
newState
);
}
handleInputChange(event){
var newState = this.state;
newState.values[event.target.name] = event.target.value;
this.setState(
newState
);
}
saveChanges(e){
e.preventDefault();
}
}
Are you using React v.14 or above? the props object is now frozen and cant be changed. You can use React.cloneElement
instead
The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .