Reputation: 1224
I have a button in a child component for debugging purposes that prints the current state in its parent component. When it prints, that is how I want the state to be. When I hit another button in the same child component, the state changes in the parent, by removing a few properties.
Notice how the Id
and SEO_CSEO_Survey_Questions__r
properties are now missing.
Child Component
import React, { Component } from 'react';
import { Link } from 'react-router';
class Index extends Component {
constructor(props){
super(props)
this.state = {
selected: 'created'
}
this.updatePreview = this.updatePreview.bind(this);
this.renderEditArea = this.renderEditArea.bind(this);
}
isActive(value) {
return 'slds-tabs--default__item '+((value===this.state.selected) ?'slds-active':'');
}
updatePreview(e){
const updatedPreview = {
...this.props.selectedPreview,
[e.target.name]: e.target.value
};
this.props.update(updatedPreview)
}
// determines which type of edit area should display
// survey settings or question
renderEditArea() {
let selected = this.props.selectedPreview;
let hasNameKey = "Name" in selected;
if(hasNameKey){
return (
<div>
<input
onChange={(e) => this.updatePreview(e)}
name="Name"
type="text"
className="slds-input"
value={selected.Name ? selected.Name : ''}
/>
<input
onChange={(e) => this.updatePreview(e)}
name="SEO__Welcome_Text__c"
type="text"
className="slds-input"
value={selected.SEO__Welcome_Text__c ? selected.SEO__Welcome_Text__c : ''}
/>
</div>
)
}else {
return (
<input
onChange={(e) => this.updatePreview(e)}
name="SEO__Question__c"
type="text"
className="slds-input"
value={selected.SEO__Question__c ? selected.SEO__Question__c : ''}
/>
)
}
}
render() {
return (
<div className="slds-size--1-of-1 slds-medium-size--4-of-5 slds-large-size--4-of-5">
<div className="slds-tabs--default">
<h2 className="slds-text-heading--small slds-p-top--x-small" style={{position: "absolute"}}>
<button onClick={this.props.addQuestion} className="slds-button slds-button--icon slds-p-left--xx-small" title="add sur">
<svg className="slds-button__icon slds-button__icon--medium" aria-hidden="true">
<use xlinkHref={addIcon}></use>
</svg>
<span className="slds-assistive-text">Add Question</span>
</button>
</h2>
<ul className="slds-tabs--default__nav" style={{justifyContent: "flex-end"}}>
<Link to="/"className="slds-button slds-button--neutral">Back</Link>
<button onClick={this.props.save} className="slds-button slds-button--brand">Save</button>
<button onClick={this.props.currentState} className="slds-button slds-button--brand">Current State</button>
</ul>
</div>
<div className="slds-grid slds-wrap slds-grid--pull-padded">
{this.renderEditArea()}
</div>
</div>
);
}
}
export default Index;
Parent
import React, { Component } from 'react';
import { getQuestions, addQuestion, deleteQuestion, newSurveyQuestions, updateSurveyQuestions, EMPTY_SURVEY } from './helpers';
import SideBar from './survey/SideBar';
import MainArea from './survey/Index';
class Survey extends Component {
constructor(props) {
super(props)
this.state = {
survey: [],
selectedPreview: []
}
this.componentWillMount = this.componentWillMount.bind(this);
this.save = this.save.bind(this);
this.setSelectedPreview = this.setSelectedPreview.bind(this);
this.currentState = this.currentState.bind(this);
}
// if the url is `/survey/new`, create an empty survey
// to save for later.
// else if there is an id in the url, load the survey and questions
componentWillMount(){
if(this.props.pathname === "/survey/new") {
this.setState({
survey: EMPTY_SURVEY,
selectedPreview: EMPTY_SURVEY[0]
})
} else if (this.props.params.surveyId){
getQuestions(this.props.params.surveyId).then(survey => {
// 'survey' contains all the questions
this.setState({
survey,
selectedPreview: survey[0]
});
});
}
}
currentState() {
console.log('clicking Current State');
console.log(this.state.survey[0]);
}
// saves a new survey with associated newly created questions
// or saves an existing survey with associated questions
save() {
console.log('clicking Save');
console.log(this.state.survey[0]);
// if the url is set to survey/new
// save new survey with associated newly created questions
if(this.props.pathname === "/survey/new") {
newSurveyQuestions(this.state.survey).then( id => {
this.context.router.transitionTo(`/survey/id/${id}`);
})
// else update survey and questions
} else {
updateSurveyQuestions(this.state.survey);
}
}
// sets selectedPreview for the entire component and
// its children
setSelectedPreview(selectedPreview) {
this.setState({selectedPreview});
}
render() {
return (
<div className="slds-grid slds-wrap slds-grid--pull-padded">
<SideBar
survey={this.state.survey}
setSelectedPreview={this.setSelectedPreview}
deleteQuestion={this.deleteQuestion}
/>
<MainArea
addQuestion={this.addQuestion}
selectedPreview={this.state.selectedPreview}
update={this.update}
save={this.save}
currentState={this.currentState}
/>
</div>
);
}
}
Survey.contextTypes = {
router: React.PropTypes.object
}
export default Survey;
help function
export function updateSurveyQuestions(survey) {
// create proper url for update request
const requestURL = `${URL + survey[0].attributes.url}`;
// save questions for later update requests
const questions = survey[0].SEO__CSEO_Survey_Questions__r.records;
let s = [...survey];
// remove properties for proper body format
delete s[0].Id;
delete s[0].SEO__CSEO_Survey_Questions__r;
delete s[0].attributes;
axios.patch(requestURL, s[0], API_TOKEN);
questions.forEach( question => {
// save request url for later
let requestURL = `${URL + question.attributes.url }`;
// remove properites for proper body format
delete question.attributes;
delete question.Id;
axios.patch(requestURL, question, API_TOKEN);
})
}
When I removed all the code in save()
, except for the console.log
's, it prints as expected.
Upvotes: 0
Views: 88
Reputation: 817228
tl;dr: Your code is working fine. What you are seeing is a consequence of how objects and the console work. Do
console.log(this.state.survey[0].Id);
instead to see that the property does actually exist.
See also Is Chrome's JavaScript console lazy about evaluating arrays?
When I removed all the code in save(), except for the console.log's, it prints as expected.
That seems to suggest that that code is changing the object. E.g. updateSurveyQuestions
or newSurveyQuestions
might remove these properties.
You have to keep in mind that the output you see in the console is computed at the moment you expand the properties, not at the moment console.log
is called. In fact, the little i
icon next to Object
tells you that. When you hover over it says:
Value below was evaluated just now
Here is a simplified example of what you are experiencing: Expand the object and notice that it is empty even though we removed it after calling console.dir
(open your browser's console):
var obj = {foo: 42};
console.dir(obj);
delete obj.foo;
Ideally updateSurveyQuestions
or newSurveyQuestions
would not mutate the object directly, but rather clone it, make the necessary changes to the clone and then update the components state (if desired). E.g. a simple way to clone the object is via Object.assign
:
var copy = Object.assign({}, this.state.survey[0]);
Upvotes: 1