Safe to set on this.state just to keep track? - reactjs

I was wondering if it is safe to set .state when I need to keep track.
My component, allows user to click it, every time they do it increments tieId. On mouse exit of the component, if the tieId is different from when they mouse enetered the component, I want to do a save.
I had to keep track on this.state
Is this code approptiate or will it break the React diff checking as I didn't use this.setState{tieId: ...}?
var HelloMessage = React.createClass({
getInitialState: function() {
return {tieId: -1, onEnt: -1};
},
click: function(e) {
this.state.tieId++;
if (this.state.tieId > 2) {
this.state.tieId= -1;
}
},
mouseEnter: function(e) {
this.state.onEnt = this.state.tieId
},
mouseLeave: function(e) {
if (this.state.tieId != this.state.onEnt) {
alert('ok saving');
} else {
alert('will not save because tie id is same as when entered \n tieId: ' + this.state.tieId + ' \n onEnt: ' + this.state.onEnt);
}
},
render: function() {
return <div onClick={this.click} onMouseLeave={this.mouseLeave} onMouseEnter={this.mouseEnter} >Hello </div>;
}
});
ReactDOM.render(<HelloMessage />, mountNode);
Can copy paste this code at the React site Live JSX editor - http://facebook.github.io/react/

You should always use setState() to make changes to your component state in React.
In case you don't believe me, here's Dan Abramov saying the same thing :)
(There's an exception if you're using ES6 classes: your constructor should set state directly.)

Related

React form validation still adds values

So I have a little bit of form validation going on and I am running into an issue. When I first load the web app up and try adding a value and submitting with my button it doesn't allow me and gives me the error I want to see. However, when I add a value setState occurs and then my value is pushed to UI and I try to add another blank value it works and my conditional logic of checking for an empty string before doesn't not go through what am I doing wrong?
addItem() {
let todo = this.state.input;
let todos = this.state.todos;
let id = this.state.id;
if (this.state.input == '') {
alert("enter a value");
document.getElementById('error').style.color = 'red';
document.getElementById('error').innerHTML = 'Please enter something first';
}
else {
this.setState({
todos: todos.concat(todo),
id: id + 1,
}, () => {
document.getElementById('test').value = '';
})
console.log(this.state.id);
}
}
You are checking this.state.input but no where in that code are you setting the input value on the state.
Try adding this where it makes sense in your application:
this.setState({ input: 'some value' });
Also, I recommend you use the state to define the application UI. So instead of using document.getElementById('error') or document.getElementById('test').value, have the UI reflect what you have in your state.
See here for more info: https://reactjs.org/docs/forms.html
Instead of manipulating the DOM directly:
document.getElementById('test').value = '';
you'll want to use React:
this.setState({ input: '' });
A good ground rule for React is to not manipulate the DOM directly through calls like element.value = value or element.style.color = 'red'. This is what React (& setState) is for. Read more about this on reactjs.org.
Before you look for the solution of your issue, I noticed that you are directly updating the DOM
Examples
document.getElementById('error').style.color = 'red';
document.getElementById('error').innerHTML = 'Please enter something first';
document.getElementById('test').value = '';
Unless you have special use case or dealing with external plugins this isn't recommended, when dealing with React you should update using the virtual DOM. https://www.codecademy.com/articles/react-virtual-dom
Pseudo code sample
constructor(props) {
this.state = {
// retain previous states in here removed for example simplicity
errorString: ''
}
}
addItem() {
let todo = this.state.input;
let todos = this.state.todos;
let id = this.state.id;
if (this.state.input == '') {
alert("enter a value");
this.setState({
errorString: 'Please enter something first'
});
}
else {
this.setState({
todos: todos.concat(todo),
id: id + 1,
input: '',
});
}
}
// notice the "error" and "test" id this could be omitted I just added this for your reference since you mentioned those in your example.
render() {
return (
<div>
{(this.state.errorString !== '') ? <div id="error" style={{color: 'red'}}>{this.state.errorString}</div> : null}
<input id="test" value={this.state.input} />
</div>
}
Every time you invoke setState React will call render with the updated state this is the summary of what is happening but there are lot of things going behind setState including the involvement of Virtual DOM.

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