How to transfer props in react v0.13? - javascript

I'm trying to learn react for my first javascript project and as a start creating a very simple code that adds two numbers entered in a text box. The result is re-rendered as a number is typed. This worked for me on react v0.11.
var App = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {
payment: 0,
payment2: 0
};
},
render: function() {
var total = parseInt(this.state.payment, 10) +
parseInt(this.state.payment2, 10);
return (
<div>
<Payment {...this.props} valueLink={this.linkState('payment')}/><span>+</span>
<Payment {...this.props} valueLink={this.linkState('payment2')}/><span>=</span>
{ total }
</div>
);
}
});
var Payment = React.createClass({
render: function() {
return this.transferPropsTo(
<input type="text" />
);
}
});
React.render(
<App />,
document.getElementById('app')
);
However, it seems like the transferPropsTo() function was removed in v0.13. How do I do the equivalent in the latest version.

You can pass {...this.props} in the input tag:
var Payment = React.createClass({
render: function() {
return (
<input type="text" {...this.props} />
);
}
});
This uses the JSX spread attributes feature.

Related

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>

map images concatenating strings

I am using the map function to iterate over an array with images. I am then trying to display these images on the page.
You will see that the images are being concatenated into one stain. Does anyone know how to do this so i can show each individual image?
I have set up a jsfiddle here
Please see React code below:
var App = React.createClass({
render: function(){
var images = [
{
id:"1",
images:['https://i.scdn.co/image/dc284fcd7e581aa2b7ca56f28c7c74f0ca0ad393', 'https://i.scdn.co/image/97ffc63dd5abfe7203d8f5e90d1a74427ac756e7']
},
{
id:"2",
images:['https://i.scdn.co/image/97ffc63dd5abfe7203d8f5e90d1a74427ac756e7', 'https://i.scdn.co/image/97ffc63dd5abfe7203d8f5e90d1a74427ac756e7']
}
];
return(<List images={images} />)
}
});
var List = React.createClass({
render: function() {
var images = this.props.images.map(function(image){
return(image.images[0]); // updated here
})
return(
<div>
<img src={images}></img>
<p>{images}</p>
</div>
)
}
});
ReactDOM.render(
<App name="World" />,
document.getElementById('container')
);
Does the following work? https://jsfiddle.net/petebere/19fmn5qs/
The change was to add the <img /> tag with the src attribute set to the url:
var images = this.props.images.map(function(image, index){
return <img key={index} src={(image.images[0])} />;
});
Also, you now don't need the <img /> tag in your final return statement. Please see my updated fiddle. I've also added the key attribute as required in the React docs: https://facebook.github.io/react/docs/multiple-components.html#dynamic-children
Full code:
var App = React.createClass({
render: function() {
var images = [
{
id:"1",
images:['https://i.scdn.co/image/dc284fcd7e581aa2b7ca56f28c7c74f0ca0ad393', 'https://i.scdn.co/image/97ffc63dd5abfe7203d8f5e90d1a74427ac756e7']
},
{
id:"2",
images:['https://i.scdn.co/image/97ffc63dd5abfe7203d8f5e90d1a74427ac756e7', 'https://i.scdn.co/image/97ffc63dd5abfe7203d8f5e90d1a74427ac756e7']
}
];
return(<List images={images} />);
}
});
var List = React.createClass({
render: function() {
var images = this.props.images.map(function(image, index){
return <img key={index} src={(image.images[0])} />;
});
return(
<div>
<p>Your images</p>
{images}
</div>
);
}
});
ReactDOM.render(
<App name="World" />,
document.getElementById('container')
);

React: Search Results not being displayed?

So I am learning react.js, and I am developing a quick search engine using the GitHub API of users.
The API side of the project works fine (I have tested by manually entering names into the area)
Its the search build in react that is not working.
(FYI: I am using Plunker which has react support)
script.jsx
var Card = React.createClass({
getInitialState: function(){
return{};
},
componentDidMount: function(){
var component = this;
$.get("https://api.github.com/users/" + this.props.login, function(data){
component.setState(data);
});
},
render: function(){
return(
<div>
<img src={this.state.avatar_url} width="100"/>
<h3>{this.state.name}</h3>
<hr/>
</div>
);
}
});
var Form = React.createClass({
handleSubmit: function(e){
e.preventDefault();
var loginInput = React.findDOMNode(this.refs.login);
this.props.addCard(loginInput.value);
loginInput.value = '';
},
render: function(){
return(
<form onSubmit={this.handleSubmit}>
<input placeholder="Enter Github Name" ref="login"/>
<button>Search</button>
</form>
);
}
});
var Main = React.createClass({
getInitialState: function(){
return {logins: []};
},
addCard: function(loginToAdd){
this.setState({logins: this.state.logins.concat(loginToAdd)});
},
render: function() {
var cards = this.state.logins.map(function(login){
return (<Card login={login} />);
});
return(
<div>
<Form addCard={this.addCard} />
{cards}
</div>
)
}
});
ReactDOM.render(<Main />, document.getElementById("root"));
The problem was (if you check console), that you had a duplicate script tag in the <head> which you didn't need. And also, you were doing React.findDOMNode instead of ReactDOM.findDOMNode
Line 25 of your JSX file:
var loginInput = ReactDOM.findDOMNode(this.refs.login);
That said, you don't need to do ReactDOM.findDOMNode. You can just use this.refs.login

React-App: Prop is updated after the next event

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>
)
}

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.

Categories

Resources