React-App: Prop is updated after the next event - javascript

I've made this "Currency-Converter" to get an idea of how React works.
It works (more or less) but the result is shown with an offset:
You type "1" (Euro) => It shows "0 Dollar".
You type "10" => It shows "1.1308 Dollar".
You type "100" => It shows "11.308 Dollar".
...
var Display = React.createClass({
render: function() {
return (
<div>
<p>{this.props.euro + ' Euro are equal to ' + this.props.dollar + ' Dollar.'}</p>
</div>
)
}
});
var Converter = React.createClass({
getInitialState: function() {
return { euro: 0, dollar: 0, exchangeRate: 1.1308 }
},
convertEuroToDollar: function() {
this.setState({ euro: +document.querySelector('#amount-euro').value });
this.setState({ dollar: this.state.euro * this.state.exchangeRate });
},
render: function() {
return (
<div>
<input type="text" id="amount-euro" onKeyUp={this.convertEuroToDollar} />
<Display dollar={this.state.dollar} euro={this.state.euro} exchangeRate={this.state.exchangeRate} />
</div>
)
}
});
ReactDOM.render(
<Converter />,
document.querySelector('#app')
);
div {
margin: 30px 50px;
}
<div id="app"></div>
Live-Demo on CodePen: http://codepen.io/mizech/pen/vGbJxe
It should display the result (euro * exchangeRate) at once.
What I'm doing wrong here?

Calling two setStates one after all, you wasn't setting the euro state properly.
Being async, you was still using the old value of it.
From the docs:
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value.
https://facebook.github.io/react/docs/component-api.html
To fix the problem, do:
convertEuroToDollar: function() {
const euro = +document.querySelector('#amount-euro').value
this.setState({
euro: euro,
dollar: euro * this.state.exchangeRate
});
},
Fixed example: http://codepen.io/FezVrasta/pen/xVeMMX
Second problem I see, it would be much better to use ref instead of document.querySelector.
convertEuroToDollar: function() {
const euro = +this.refs.amountEuro.value;
this.setState({
euro: euro,
dollar: euro * this.state.exchangeRate
});
},
render: function() {
return (
<div>
<input type="text" ref="amountEuro" onKeyUp={this.convertEuroToDollar} />
<Display dollar={this.state.dollar} euro={this.state.euro} exchangeRate={this.state.exchangeRate} />
</div>
)
}

Related

ReactJs how to show list with load more option

I am tring to show todo list with load more option. I am appling limit.Limit is apply to list.But when i add loadmore()function. then i get error this.state.limit is null Wher i am wrong.Any one can suggest me.
here is my code
todoList.jsx
var TodoList=React.createClass({
render:function(){
var {todos}=this.props;
var limit = 5;
function onLoadMore() {
this.setState({
limit: this.state.limit + 5
});
}
var renderTodos=()=>{
return todos.slice(0,this.state.limit).map((todo)=>{
return(
<Todo key={todo.todo_id}{...todo} onToggle={this.props.onToggle}/>
);
});
};
return(
<div>
{renderTodos()}
<a href="#" onClick={this.onLoadMore}>Load</a>
</div>
)
}
});
module.exports=TodoList;
Changes:
1. First define the limit in state variable by using getInitialState method, you didn't define the limit, that's why this.state.limit is null.
2. Define all the functions outside of the render method.
3. Arrow function with renderTodos is not required.
4. Use this keyword to call the renderTodos method like this:
{this.renderTodos()}
Write it like this:
var TodoList=React.createClass({
getInitialState: function(){
return {
limit: 5
}
},
onLoadMore() {
this.setState({
limit: this.state.limit + 5
});
},
renderTodos: function(){
return todos.slice(0,this.state.limit).map((todo)=>{
return(
<Todo key={todo.todo_id}{...todo} onToggle={this.props.onToggle}/>
);
});
};
render:function(){
var {todos} = this.props;
return(
<div>
{this.renderTodos()}
<a href="#" onClick={this.onLoadMore}>Load</a>
</div>
)
}
});
This is witout button click.
As you all know react components has a function componentDidMount() which gets called automatically when the template of that component is rendered into the DOM. And I have used the same function to add the event listener for scroll into our div iScroll.
The scrollTop property of the element will find the scroll position and add it with the clientHeight property.
Next, the if condition will check the addition of these two properties is greater or equal to the scroll-bar height or not. If the condition is true the loadMoreItems function will run.
class Layout extends React.Component {
constructor(props) {
super(props);
this.state = {
items: 10,
loadingState: false
};
}
componentDidMount() {
this.refs.iScroll.addEventListener("scroll", () => {
if (this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >=this.refs.iScroll.scrollHeight){
this.loadMoreItems();
}
});
}
displayItems() {
var items = [];
for (var i = 0; i < this.state.items; i++) {
items.push(<li key={i}>Item {i}</li>);
}
return items;
}
loadMoreItems() {
this.setState({ loadingState: true });
setTimeout(() => {
this.setState({ items: this.state.items + 10, loadingState: false });
}, 3000);
}
render() {
return (
<div ref="iScroll" style={{ height: "200px", overflow: "auto" }}>
<ul>
{this.displayItems()}
</ul>
{this.state.loadingState ? <p className="loading"> loading More Items..</p> : ""}
</div>
);
}
}
This is example

Get input value on Click in React

I'm trying to get the value of input onClick(not onChange) and I got really confused.
(the function should take user input and multiply it by 5)
var Multiply = React.createClass({
getInitialState: function(){
return {number: '-'}
},
multiply: function(a){
return (a * 5);
},
handleChange: function(e) {
this.setState({ number: e.target.value });
},
handleClick: function(e) {
this.multiply(this.state.number);
},
render: function () {
return (
<div>
<p>multiply by 5!</p><br />
<textarea onChange={this.handleChange}>
</textarea><br/>
<button onClick={this.handleClick}>Submit</button>
<p>result:</p> {this.state.result}
</div>
);
}
});
ReactDOM.render(
<Multiply/>,
document.getElementById('root')
);
Here is a link to code:
http://codepen.io/polinaz/pen/MmezZy?editors=0010
What's wrong with my logic? Will really appreciate a good explanation!
Thank you
When you click the button, it multiples the number by 5, but then you don't do anything with that multiplied number (the result). In your handleClick function you need something like this:
handleClick: function(e) {
this.setState({result: this.multiply(this.state.number)});
}
I added result to the state, and added a result variable into the multiply function so you can store it in the state. Please look below:
var Multiply = React.createClass({
getInitialState: function(){
//added result to the initial state here
return {number: '-',
result: 0}
},
multiply: function(a){
//added result var here, and set the state to accept this result
var result = a * 5;
this.setState({result: result});
},
handleChange: function(e) {
this.setState({ number: e.target.value });
},
handleClick: function(e) {
this.multiply(this.state.number);
},
render: function () {
return (
<div>
<p>multiply by 5!</p><br />
<textarea onChange={this.handleChange}>
</textarea><br/>
<button onClick={this.handleClick}>Submit</button>
<p>result:</p> {this.state.result}
</div>
);
}
});
ReactDOM.render(
<Multiply/>,
document.getElementById('root')
);

Cannot interact between react components

This is for a react JS project (jsfiddle). The textbox should update with the true/false checked value of the checkbox, but it does not do so. Can someone explain why?
var AutoGenerateCheckbox = React.createClass ({
getInitialState: function() {
return {checked: false};
},
update() {
this.state.checked = !this.state.checked;
alert(this.state.checked);
this.props.onUpdate(this.state.checked);
},
render() {
return (
<input type="checkbox" checked={this.state.checked} onChange={this.update} />
);
}
});
var TBox = React.createClass({displayName: 'TextBox',
render: function() {
return (
<div>
Checkbox value: {this.props.data}
</div>
);
}
});
var KApp = React.createClass({
getInitialState: function() {
return {autoChecked: false};
},
handleAutogenChange: function(val) {
alert('handleAutogenChange:' + val);
this.setState({autoChecked : val});
},
render: function() {
return (
<div>
<AutoGenerateCheckbox onUpdate={this.handleAutogenChange}/>
<TBox data={this.state.autoChecked}/>
</div>
);
}
});
ReactDOM.render(
<KApp />,
document.getElementById('content')
);
The reason you don't see anything printed out is because you are trying to print a boolean value here
<div>
Checkbox value: {this.props.data}
</div>
try
<div>
Checkbox value: {this.props.data.toString()}
</div>
instead.
As an extra tip, you don't really need to hold the state of the checkbox in both its own state and its parent component's state. You really only need to have it in the parent component's state.
See the fiddle I made.
React is not determining the Boolean value to be printable information, try this instead:
<div>
Checkbox value: {this.props.data.toString()}
</div>

React JS: Reusable components

I have created the form validation with a structure like this
var Signin = React.createClass({
render: function() {
return (
<Form>
<Input type="text" name="email" labelName="Email" rules="isEmail" error:"Email not valid" />
<Input type="password" name="password" labelName="Password" rules="isLength:6" error:"Passowrd not valid"/>
</Form>
);
}
});
because, for example, the "Email" input will be used in different part of application, I would avoid to add the same attributes (name, type, labelName, rules and error) every time. So I would create something like this
var InputEmail = React.createClass({
render: function () {
return (
<Input type="text" name="email" labelName="Email" rules="isEmail" error="Email not valid"/>
)
}
});
var InputPassword = React.createClass({
render: function () {
return (
<Input type="password" name="password" labelName="Password" rules="isLength:6" error="Passwordnot valid"/>
)
}
});
So the Signin Component should be
var Signin = React.createClass({
render: function() {
return (
<Form>
<InputEmail />
<InputPassword />
</Form>
);
}
});
but in this way, I get two errors:
I can't find anymore in the Form the props.name of Input because
there isn't in InputEmail;
in the render function of Input the state is null
How could I create a reausable/inherits components? I failed using both the composition pattern and the mixins
I added my full code: Form
var Form = React.createClass({
getInitialState: function () {
return {
isValid : false,
isSubmitting: false
}
},
componentWillMount: function(){
this.model = {};
this.inputs = {};
this.registerInputs(this.props.children);
},
registerInputs: function(children){
React.Children.forEach(children, function (child) {
if (child.props.name) {
child.props.attachToForm = this.attachToForm;
child.props.detachFromForm = this.detachFromForm;
child.props.validate = this.validate;
}
if (child.props.children) {
this.registerInputs(child.props.children);
}
}.bind(this));
},
attachToForm: function (component) {
this.inputs[component.props.name] = component;
this.model[component.props.name] = component.state.value;
this.validate(component);
},
detachFromForm: function (component) {
delete this.inputs[component.props.name];
delete this.model[component.props.name];
},
validate: function (component) {
var isValid = true;
// validation code
component.setState({
isValid: isValid,
}, this.validateForm);
},
validateForm: function () {
var formIsValid = true;
var inputs = this.inputs;
Object.keys(inputs).forEach(function (name) {
if (!inputs[name].state.isValid) {
formIsValid = false;
}
});
this.setState({
isValid: formIsValid
});
},
updateModel: function (component) {
Object.keys(this.inputs).forEach(function (name) {
this.model[name] = this.inputs[name].state.value;
}.bind(this));
},
submit: function (event) {
event.preventDefault();
this.setState({
isSubmitting : true
});
this.updateModel();
console.log(this.model);
},
render: function () {
return (
<form className="ui form" onSubmit={this.submit}>
{this.props.children}
<button className="ui button" type="submit" disabled={this.state.isSubmitting}>Accedi</button>
</form>
);
}
});
Input
var Input = React.createClass({
getInitialState: function(){
return {
value : this.props.value || "",
isValid: true
}
},
setValue: function (event) {
this.setState({
value: event.target.value
}, function () {
this.props.validate(this);
}.bind(this));
},
componentWillMount: function () {
if (this.props.required) {
this.props.validations = this.props.validations ? this.props.validations + ',' : '';
this.props.validations += 'isLength:1';
}
// ERROR: TypeError: this.props.attachToForm is not a function
this.props.attachToForm(this);
},
componentWillUnmount: function () {
this.props.detachFromForm(this);
},
render: function () {
var className = "field";
if(this.props.className){
className += " " + this.props.className;
}
if(this.props.required){
className += " required";
}
var Label;
if(this.props.labelName){
Label = (<label htmlFor={this.props.name}>{this.props.labelName}</label>);
}
var Error;
if(!this.state.isValid){
Error = (<div className="ui">{this.props.error || this.props.name + " not valid"}</div>);
};
return (
<div className={className}>
{Label}
<input type={this.props.type || "text"} id={this.props.name} name={this.props.name} onChange={this.setValue} value={this.state.value} />
{Error}
</div>
);
}
});
With this works
ReactDOM.render(
<Form>
<Input type="text" name="email" labelName="Email" rules="isEmail" error:"Email not valid"/>
</Form>,
document.getElementById('app')
);
In this way I get:
"TypeError: this.props.attachToForm is not a function
this.props.attachToForm(this);"
ReactDOM.render(
<Form>
<InputEmail/>
</Form>,
document.getElementById('app')
);
P.S: I tried to add this code on jsfiddle but I get "!TypeError: can't define property "attachToForm": Object is not extensible"
jsfiddle
There are 2 main issues with your setup:
Your <Form> is set up in such a way, that the children of the form need to have props, otherwise it does not work.
The <InputEmail> wrapper is incomplete. It needs to pass along all props to the <Input>, including the Form functions passed down.
Ad 1: Fix the form, to ensure validation methods are added
The reason you get the error is because the children of your <Form> need to have props.name. It then registers the functions of the form (including attachToForm), by adding them to the children. This is done in the method registerInputs().
In the original variant, the <Input> component has props, so all goes well.
In the adapted variant, the wrapper <InputEmail> no longer has props, so the attachToForm() and other functions are not added to props, and you get the error when the <Input> tries to invoke the function.
Simplest way to fix this: add at least 1 prop in the render function, and check this in the registerInputs(), e.g.:
ReactDOM.render(
<Form>
<InputEmail initialValue={'name#domain.com'}/>
</Form>,
document.getElementById('app')
);
And in registerInputs(), change the line:
if (child.props.name) {
to:
if (child.props.initialValue) {
2. Extend <InputEmail> wrapper to pass down functions as well
Simplest way to do this is to add {...this.props}, like this:
var InputEmail = React.createClass({
render: function () {
return (
<Input {...this.props}
type="text" name="email" labelName="Email" rules="isEmail" error="Email not valid"/>
)
}
});
That way, the functions passed down by <Form> to the <InputEmail> component (as well as any other props), will be passed down to the <Input> component.
PS: The code inside registerInputs() that checks for child.props.children does not work as intended: at the time it is invoked, the <InputEmail> component does not have children. Like the name implies, it checks for children passed down as props. And the only prop passed was initialValue.
As minor issues I would suggest to make 2 more changes:
In registerInputs(), you directly modify props. This is generally not a good idea. Better is to make a copy of props, and add your form-methods to the copy. You can use React.Children.map to do this. See official docs here.
Instead of hard-coding the name="email" etc of your <Input> component, inside <InputEmail>, better is to put the default values of these in default values of props, using propTypes, as explained here in official docs.

Display element on increase of counter in ReactJS

I'm new to ReactJS. I need to show a text as many times as user clicks. I have tried but some how it is not working as expected. Please help.
var Paragraph = React.createClass({
renderParagraph: function(){
for(i=0; i<this.props.data;i++){
<p key={i}> Hello World </p>
}
},
render: function() {
return(
<div>
{this.renderParagraph}
</div>
);
}
});
var Container = React.createClass({
getInitialState: function() {
return {
counter: 0
}
},
increment: function() {
this.setState({
counter: this.state.counter + 1
});
},
render: function() {
return (
<div className="well">
<Paragraph data={this.state.counter}/>
<button className="btn btn-primary" onClick={this.increment}> Increase Me
</button>
</div>
)
}
});
React.render(<Container />, document.getElementById('root'));
In the above code, i'm trying to render Paragraph component as many times as user clicks.
Implementation here: http://jsbin.com/jevufu/edit?js,output
Thanks in advance.
You need call method this.renderParagraph() not just pass reference this.renderParagraph., also, from method renderParagraph you need return array with Nodes,
var Paragraph = React.createClass({
renderParagraph: function() {
var paragraphs = [];
for (var i = 0; i < this.props.data; i++) {
paragraphs.push(<p key={i}> Hello World { i } </p>);
}
return paragraphs;
},
render: function() {
return(
<div>{ this.renderParagraph() }</div>
);
}
});
Example

Categories

Resources