accessing state data in componentDidMount - reactjs

Why is it not possible to access this.state.board within componentDidMount? As I understand it, once the component has been rendered, componentDidMount is fired immediately after, and only once.
I am trying to set up a Google Analytics ecommerce tracker, and so I thought the best place to set that up would be within the componentDidMount because it ensures that the GA tracker is called only once. However, I am not able to access any of the state data to send back to GA. Any ideas?
//function which establishes state
function getEditorState() {
var board = Editor.getCsb();
var similarBoard = Editor.getSimilarLsbs();
return {
board: board,
similarBoard: similarBoard,
editing: Editor.isEditing(),
hoverColor: Editor.getHoverColor(),
variant: Editor.variant(),
lidAndLsb: Editor.getLidAndLsb()
};
}
//component
var CsbEditorApp = React.createClass({
getInitialState: function () {
return getEditorState();
},
componentDidMount: function () {
console.log(this.state.board); // <---- this returns an empty object.
Editor.addChangeListener(this._onChange);
SbAction.loadCsb(this.props.params.cid);
},
render: function() {
console.log(this.state.board); // <---- this returns the the board object with data.
return (
<div className={cm('lightbox sb-bg overlay-border')} ref="app">
<Header board={this.state.board} label={this.state.label} socialLinkStatus={this.state.socialLinkStatus} buyingAll={this.state.buyingAll} />
<div className="viewer-content">
<div id="csb-content">
<MetaText className="meta-author" metaKey="author" board={this.state.board} />
<BoardMeta board={this.state.board}/>
<CsbPanel board={this.state.board hoverColor={this.state.hoverColor} showPanel={showPanel} />
<RouteHandler/>
</div>
</div>
</div>
);
},
_onChange: function () {
this.setState(getEditorState());
$("#cc_hover").hide();
}
});

console.log is not a reliable method of debugging - the method is async and actually can get called after you've set up your listener or even after the callback it registers (which affects the state) has been triggered, so use the debugger. Also, try commenting out the Editor.addChangeListener(this._onChange); line and see if it causes the problem.

Related

React prop array length returning 0 [duplicate]

This question already has an answer here:
Can't iterate over my array/object. Javascript, React Native [duplicate]
(1 answer)
Closed 5 years ago.
I have an array prop on a component called jobs that will show in the console, but always returns length:0
The Image you will see definitely has three elements in the array, but when I attempt to access the array length through console.log(this.props.jobs.length);
Why do the elements of this array log out, but I can't access the elements in code?
Per request from #finalfreq, see full code below:
const DepartmentContainer = React.createClass({
getInitialState:function(){
return{sortedJobs:{}, loaded:false};
},
componentDidMount:function(){
//console.log(this.state.sortedJobs);
var departments = this.props.departments;
departments.map(function(name, index){
this.state.sortedJobs[name] = [];
}, this)
var _this = this;
axios.get('{api call returning json}')
.then(function (response) {
for(let i=0;i<response.data.jobs.length;i++){
for(let j=0;j<response.data.jobs[i].metadata[0].value.length;j++){
_this.state.sortedJobs[response.data.jobs[i].metadata[0].value[j]].push(response.data.jobs[i]);
}
}
})
.catch(function (error) {
console.log(error);
});
//console.log(Object.keys(_this.state.sortedJobs).length);
this.setState({sortedJobs: _this.state.sortedJobs});
this.setState({loaded:true});
},
render:function(){
var departments = this.state.sortedJobs;
return(
<div>
{this.state.loaded ?
<div className="row grid-12">
{Object.keys(departments).map(function(label, department){
//console.log(departments[label]);
return <Department key={label} title={label} jobs={departments[label]}/>
})}
</div>
:
<div>Test</div>
}
</div>
);
}
});
const Department = React.createClass({
getInitialState:function(){
return{hidden:false, hasJobs:false};
},
componentWillMount:function(){
const jobs = this.props.jobs;
if(jobs.length>0){
this.setState({hasJobs:true});
}
},
componentDidMount:function(){
console.log(this.state.hasJobs);
console.log(this.props.jobs.length);
},
renderNormal:function(){
return(
<div className="grid-4 department-block" data-dep-filter={this.state.hidden} data-loc-filter="false" data-department={this.props.title}><h3 className="text-uppercase">{this.props.title}</h3>
<div className="posting-list margin-bottom">
<h3 className="text-uppercase">{this.props.title}</h3>
</div>
</div>
)
},
renderEmpty:function(){
return(
<div className="grid-4 department-block" data-dep-filter={this.state.hidden} data-loc-filter="false" data-department={this.props.title}><h3 class="text-uppercase">{this.props.title}</h3>
<div className="posting-list margin-bottom">
<div class="no-posts job-post">
<p><em>0 Current Openings</em></p>
</div>
</div>
</div>
);
},
render: function(e){
if (this.hasJobs) {
return (
this.renderNormal()
)
}else{
return(
this.renderEmpty()
)
}
}
});
In the Department:componentWillMount function I want to check the jobs array passed to it from the DepartmentContainer:render function and set the state on said Department to either hasJobs true/false
#finalfreq has the right idea. This is an artifact of how the JavaScript console works (for Chrome at least). Values are generally only displayed/retrieved when you expand them, which can lead to some counter intuitive behavior. You must be adding elements to the array after you are logging it to the console.
Check it out:
I make an array and push something into it. Then I log it to console. Then I push a second element.
Now when I expand the previous logged Array, you'll see it now has the most up-to-date values. Except "Array[1]" doesn't update to "Array[2]"... and if you push another value into the array, the previously logged values won't change even if you collapse and expand them again.
The moral of the story is... don't rely on console.log too much hehe. But if you do, learn its quirks.

React JS - Function within Component cannot see State

In code below the onclick function testNewBug is unable to access the state of its parent component 'BugList'. Can anyone see where I have gone wrong with this, I am correctly setting the state and can view it in DevTools, surely with the function within the component 'this.state' should be working?
class BugList extends React.Component {
constructor() {
super();
this.state = {
bugs: bugData
}
}
render() {
console.log("Rendering bug list, num items:", this.state.bugs.length);
return (
<div>
<h1>Bug Tracker</h1>
<BugTable bugs={this.state.bugs} />
<button onClick={this.testNewBug}>Add Bug</button>
</div>
)
}
testNewBug() {
var nextId = this.state.bugs.length + 1;
this.addBug({id: nextId, priority: 'P2', status:'New', owner:'Pieta', title:'Warning on console'})
}
addBug(bug) {
console.log("Adding bug:", bug);
// We're advised not to modify the state, it's immutable. So, make a copy.
var bugsModified = this.state.bugs.slice();
bugsModified.push(bug);
this.setState({bugs: bugsModified});
}
}
Oh dear I was being and idiot, I forgot to bind my event handler to 'this'
<button onClick={this.testNewBug.bind(this)}>Add Bug</button>
if you know the method will always bind to the current class instance you can always define your method like this with =>:
testNewBug = () => {
var nextId = this.state.bugs.length + 1;
this.addBug({id: nextId, priority: 'P2', status:'New', owner:'Pieta', title:'Warning on console'})
}
you won't have to worry about bind(this) all over the place and this assures the function has one instance per class.

relative time in redux reactjs

I have some data with datetime fields , i want to show the relative date time using momentJS fromNow(). However after the initial load it shows timestamp as a few seconds ago. But this will not be updated until a next state change triggered. Is it a good practice to keep another state in the state-tree & control via a timer function setInterval in componentDidUpdate?
render()
{
// get the new prop value here which triggered from a setInterval -> action -> reducer -> state change -> propagate to connected components
const text = comment.get('text');
const dateTime = moment(comment.get('dateTime')).fromNow();
return (
// add the new prop into the component
<div key={id}>
<Comment
text = {text}
dateTime = {dateTime}
</div>
}
I scribbled down a component that takes an epoch time timestamp and display a momentjs text for it.
The text is updates via inner component state every 300ms which you can change however you'd like.
You can notice on this fiddle, every new text is logged in the console. After 45 seconds you should see the text change from "a few seconds ago" to "a minute ago".
Fiddle here, this is the code:
var MomentTime = React.createClass({
getInitialState: function() {
return {text: ""};
},
componentWillMount: function() {
this._updateMomentText();
this.interval = setInterval(this._updateMomentText, 300);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
_updateMomentText: function() {
var text = moment(this.props.timestamp).fromNow()
console.log(text)
if(text !== this.state.text) {
this.setState({text: text});
}
},
render: function() {
return <div>{this.state.text}</div>;
}
});
ReactDOM.render(
<MomentTime timestamp={new Date().getTime()} />,
document.getElementById('container')
);

Uncaught TypeError: Cannot read property 'ImageRoute' of undefined

I have a simple set of components that all are composed of the following main component. The document component exposes its event handler from its props property. The event fires all the way up as expected. However once its caught in the main component, i try to set the state. Upon setting the state inside the event handler it throws an error the first time i try and retrieve the state. Every subsequent attempt works as expected.
In the example image below it shows the first time i set and try to print out the value of ImageRoute from the document object it fails then works every single time after.
selectedDocument is the eventhandler
Anybody have an explanation?
var Workstation = React.createClass({
getInitialState: function () {
return {};
},
selectedDocument :function(obj, clickedNumber){
var component = this;
console.log(obj.ImageRoute)
console.log(clickedNumber + " was clicked")
component.setState({ selectedDocument: obj });
console.log("selectedDocument set")
if (this.isMounted()) {
console.log(this.state.selectedDocument.ImageRoute)
} else {
console.log("not mounted")
}
},
render: function () {
return (
<div>
<DocumentQueue initialData={jsonData.initialData} selectedDocument={this.selectedDocument} />
<ImageViewer src={this.state.selectedDocument==null ? this.state.selectedDocument : this.state.selectedDocument.ImageRoute} />
</div>
);
}
});
you havent set the state yet. what I mean is the setState function is async and doesn't wait until the state is set before moving to the next line of code. you can use a callback function to set this up correctly
var component = this;
console.log(obj.ImageRoute)
console.log(clickedNumber + " was clicked")
component.setState({ selectedDocument: obj }, function(){
console.log("selectedDocument set")
if (component.isMounted()) {
console.log(component.state.selectedDocument.ImageRoute)
} else {
console.log("not mounted")
}
});

i need React databinding (Arr push after data refresh)

I am faced with the problem
web page is to react with the signal.
Signal does not regularly.
my Scenarios (Arr data push after refresh)
It does not give any one event
Because I can not use. setState funciton
i think javascript function call for react databind refresh
Because the data binding, you use the following dataRefresh() functions.
I know code is incorrect.
I've written code like the following:
var dataArr = [{
key : '1',
text : "hello1",
title: "title1"
},
{
key : '2',
text : "hello2",
title: "title2"
},
{
key : '3',
text : "hello3",
title: "title3"
}
];
var Repeat = React.createClass({
render : function(){
var data = this.props.items;
return(
<PanelGroup accordion >
{ data.map(function(item){
return(
<Panel header={item.title} eventKey={item.key} >
{item.text}
</Panel>
);
})}
</PanelGroup>
);
}
});
function startReact(){
React.render(
<div>
<Repeat items={ dataArr }/>
</div>,
document.getElementById('content')
);
}
startReact();
function dataRefresh(){
dataArr.push({
key : '4',
text : "hello4",
title: "title4"
});
startReact();
}
setTimeout("dataChange()",3000);
It is the question.
I need to have an idea that can solve the problem.
Advice is required.
That's a bad idea. When you have new data use setState so it will update/rerender your view automatically that's the point of react.
Bind your update to a function that update the state not directly to the render.
Here is an example very easy it explain how to update your state when the user is clicking on some button:
https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html
So for your instead of handling a user action you'll set an handler that is called when you have new data.
I hope it's clear
To go in React way, you must use state instead of that above method.
Use getInitialState to point to the global dataArr and then use setState to update the state.
Even I would suggest putting dataArr in the base component holding the child components. This will avoid polluting the global namespace as well.
Inside your setTimeout, avoid using string. instead wrap it inside a function like below:
setTimeout(function() {
dataChange();
}, 3000);
So the code will become:
var Repeater = React.createClass({
getInitialState: function() {
return {
data: dataArr
}
},
componentDidMount: function() {
setTimeout(function() {
// update the dataArr
// Instead of calling dataChange gloabl method, I would put it inside the base component and call this.updateData();
// this.setState({data: dataArr});
}.bind(this),3000);
},
updateData: function() {
// increment the array and call
this.setState({data: dataArr});
},
render : function() {
return (
<div>
<Repeat items={ this.state.data}/>
</div>
);
}
});
The below code will become:
function startReact(){
React.render(<Repeater />,
document.getElementById('content')
);
}

Resources