Accessing React component's props - javascript

Alright so I'm tinkering with ReactJS and working on simple examples, everything working great and I already feel it has improved my productivity. Now I am working on a simple React example that takes an app name and logs it to console when the Enter key press is detected. Everything working fine until I enter the app name in the input box and I press the Enter key, what I see then in the console log isn't the input value, but rather an "undefined" value. Here's the full JS Code:
"use strict";
var InputText = React.createClass({
render() {
return <div><p>Please input the app name you wish to access:</p></div>
}
});
var InputBox = React.createClass({
onKeyPress(e) {
if (e.key === 'Enter') {
console.log(this.props.value);
}
},
render() {
return <input type="text" onKeyPress={this.onKeyPress}></input>
}
});
var AppForm = React.createClass({
render() {
return <div class="appForm">
<InputText />
<InputBox />
</div>
}
});
var App = React.createClass({
render() {
return <AppForm />
}
});
ReactDOM.render(
<App />,
document.getElementById("container")
);

That's because you are not passing the value as a prop to your InputBox component.
You can get the value from the event
var InputBox = React.createClass({
onKeyPress(e) {
if (e.key === 'Enter') {
console.log('InputBox Value: ' + e.target.value);
}
},
render() {
return <input type="text" onKeyPress={this.onKeyPress}></input>
}
});
jsfiddle
Or store the value in the state and get it from there.
var InputBox = React.createClass({
onKeyPress(e) {
if (e.key === 'Enter') {
console.log('InputBox Value: ' + this.state.value);
}
},
render() {
return <input type="text" onKeyPress={this.onKeyPress} onChange={(e) => this.setState({value: e.target.value})}></input>
}
});
jsfiddle

you didnt pass any props into . You would pass props like this
there are no props passed anywhere in this app actually :)
But what you really want is the value from the input box. So in React you'd make a reference. As a quick and dirty example I have a global context object ctx={}
<input type="text" className="inputClass" style={inputStyles} ref={(c) => ctx._input = c} />
Now in my component I can refer to the value typed as
ctx._input.value
Console.log that and it should be all good.

Use ref to access the value of input box
"use strict";
var InputText = React.createClass({
render() {
return <div><p>Please input the app name you wish to access:</p></div>
}
});
var InputBox = React.createClass({
onKeyPress(e) {
if (e.key === 'Enter') {
console.log(this.refs.textbox.value);
}
},
render() {
return <input type="text" onKeyPress={this.onKeyPress} ref = 'textbox'></input>
}
});
var AppForm = React.createClass({
render() {
return <div class="appForm">
<InputText />
<InputBox />
</div>
}
});
var App = React.createClass({
render() {
return <AppForm />
}
});
ReactDOM.render(
<App />,
document.getElementById("container")
);
JSFIDDLE
Another way to obtain value will to use the event e as e.target.value. Props doesnt work because you are not actually passing props to the InputBox component.

Related

How to change a class component (input, conditional) to functional component with useState - hook?

I'd like to change a working class component into a functional component. It's basically an input field that should:
1) Dynamically display/Mirror whatever you enter (see the h1 below the form)
2) Show "no data provided" if nothing entered
The error message says "Failed to compile ... unexpected use of "event""
import React, { useState } from "react"
function Exercise2() {
const [input, inputChange] = useState({firstName: ""})
const handleChange = () => {
inputChange({[event.target.name]: event.target.value});
}
let display
if (useState.firstName != "") {
display = useState.firstName
} else {
display = "no data provided!"
}
return (
<div>
<form>
<input
type="text"
name="firstName"
placeholder="Enter your data here"
value = "input.firstName"
onChange = "handleChange"
/>
</form>
<h1>{display}</h1>
</div>
)
}
export default Exercise2
Can someone point out what I am missing (without altering the code structure too much since I want to stick to my beginners logic). Thx
PS: this was my class component which worked perfectly and that I am trying to translate
class Exercise1 extends React.Component {
constructor() {
super()
this.state = {
firstName:""
}
this.handleChange = this.handleChange.bind(this)
}
handleChange (event) {
this.setState({
[event.target.name]: event.target.value
})
}
render() {
let display
if(this.state.firstName != "") {
display=this.state.firstName
} else {
display="no data provided!"
}
return (
<div>
<form>Input:
<input
type="text"
name="firstName"
placeholder = "Enter your data here!"
value={this.state.firstName}
onChange={this.handleChange}
/>
</form>
<h1>{display}</h1>
</div>
)
}
}
So, the key things are:
1) Not placing the variable/method names in curly braces
value={input.firstName}
onChange={handleChange}
2) Not including an event arg in your handleChange method:
const handleChange = (event) => {
Here I've corrected those problems and also changed how the display is being rendered to make it more "React-like".
const { useState } = React;
function Exercise2() {
// Here you're setting state and assigning an object to it with
// once property: `firstName`
const [ input, inputChange ] = useState({ firstName: '' });
const handleChange = (event) => {
// The click event has been passed in.
// `target` is the element that's been clicked. We're just grabbing the name and
// value from it. We're assigning the name `firstName` as a dynamic property key name
// (we wrap it in square braces), and assign the value to it. We do that because we want to
// update the `firstName` property in the state object.
// Because state is an object (and we might eventually have other properties in there
// that we want to keep when we update this value) we return an object containing all
// the properties of input, and the new dynamic property
inputChange({ ...input, [event.target.name]: event.target.value });
}
// `input` is an object with a firstName property.
// We can destructure the `firstName` property from the state to make
// it easier to work with
const { firstName } = input;
console.log(input)
let display;
if (firstName.length) {
display = firstName;
} else {
display = "no data provided!";
}
return (
<div>
<form>
<input
type="text"
name="firstName"
placeholder="Enter your data here"
value={firstName}
onChange={handleChange}
/>
</form>
<h1>{display}</h1>
</div>
);
}
ReactDOM.render(<Exercise2 />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"/>
import React, { useState } from "react"
function Exercise2() {
const [input, inputChange] = useState({firstName: ""})
const handleChange = (event) => {
inputChange({[event.target.name]: event.target.value});
}
let display
if (input.firstName !== "") {
display = input.firstName
} else {
display = "no data provided!"
}
return (
<div>
<form>
<input
type="text"
name="firstName"
placeholder="Enter your data here"
value = {display}
onChange = {handleChange}
/>
</form>
<h1>{display}</h1>
</div>
)
}
export default Exercise2
You're syntax seems to be wrong, you should try something like
import React, { useState } from "react"
function Exercise2() {
const [formData, changeFormData] = useState({firstName: ""});
const handleChange = (event) => {
changeFormData({[event.target.name]: event.target.value});
}
return (
<div>
<form>
<input
type="text"
name="firstName"
placeholder="Enter your data here"
value={formData.firstName}
onChange={handleChange}
/>
</form>
<h1>{formData.firstName !== "" ? formData.firstName : 'no data provided!'}</h1>
//the above line is a ternary operator, basically it reads as if( input.firstName !== "") return input.firstName : else return 'no data provided!'
</div>
)
}
export default Exercise2

How to get react to correctly render a list of removable inputs?

I'm trying to render a list of inputs in react and bind the input values to an array. I'm also trying to make it so the list items are removable. However, when I remove an item from the array, the input items are not updated how I would expect. Instead of removing the input that was removed from the middle of the array, the last input is removed and the middle input remains.
var Inputs = React.createClass({
getInitialState: function() {
return {
inputarr: ['']
};
},
render: function() {
var self = this;
return <div>{ this.state.inputarr.map(function (value, i) {
return <div key={i}><input onChange={function (e) {self.onChangeInput(i, e)}}/>
{ i < (self.state.inputarr.length - 1) && <button onClick={function () {self.onRemove(i)}}>x</button>}
</div>;
}) }</div>;
},
onChangeInput: function (i, e) {
this.state.inputarr[i] = e.target.value;
if (this.state.inputarr[this.state.inputarr.length - 1] !== '') {
this.state.inputarr.push('');
}
this.setState({
inputarr: this.state.inputarr.slice(0)
});
},
onRemove: function (i) {
this.state.inputarr.splice(i, 1);
this.setState({
inputarr: this.state.inputarr.slice(0)
});
}
});
ReactDOM.render(
<Inputs/>,
document.getElementById('container')
);
You can run this in this fiddle: https://jsfiddle.net/vvd7hex9/1/
What happens?
add something to the first input, a second will appear. Type in 3 different inputs.
remove the second input using the x button.
The last input is removed.
What I expected to happen
The middle input to be removed and only 2 inputs should contain the contents in the inputarr array.
Why does this happen? How can I fix it to remove the correct input?
Ahhhh, this is a classic javascript problem. It has to do with your map statement. You can read more about the specific details here, but what it boils down to is that when the click events actually fire, the value of i is equal to inputarr.length - 1. To fix this, you need some way of preserving the value of i during each loop. The easiest way to do this is to change the click event to this:
<button onClick={self.onRemove(i)}>x</button>
and change onRemove to this:
onRemove: function (i) {
var self = this;
return function(e) {
self.state.inputarr.splice(i, 1);
self.setState({
inputarr: this.state.inputarr.slice(0)
});
}
}
Some more info about closures can be found here if you're unfamiliar
I think it would be better to have separate Input component and App component.
Then you can create increment and decrement methods and pass them down from App to your Input components. I have build a little pen to show how you can achieve it.
I used some useful methods from lodash so take a look how them work.
https://codepen.io/dagman/pen/oYaYyL
The code itself.
class App extends React.Component {
constructor(props) {
super(props);
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
this.state = {
quantity: [0],
};
}
increment(value) {
const { quantity } = this.state;
this.setState({
quantity: quantity.concat(_.last(quantity) + 1),
});
}
decrement(el) {
const { quantity } = this.state;
this.setState({ quantity: _.without(quantity, el) })
}
render() {
const inputs = this.state.quantity.map(x => (
<Input
increment={this.increment}
decrement={this.decrement}
key={x}
toDelete={x}
/>
));
return (
<form>
{inputs}
</form>
);
}
}
class Input extends React.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.onBtnClick = this.onBtnClick.bind(this);
this.state = {
value: '',
shouldIncrementQuantity: true,
};
}
onChange(e) {
const value = e.target.value;
this.setState({ value });
if(value.trim().length > 0 && this.state.shouldIncrementQuantity) {
this.setState({
shouldIncrementQuantity: false,
}, () => this.props.increment());
}
}
onBtnClick(e) {
e.preventDefault();
this.props.decrement(this.props.toDelete);
}
render() {
return (
<p className="input-field">
<input
type="text"
value={this.state.value}
onChange={this.onChange}
/>
<button onClick={this.onBtnClick}>x</button>
</p>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);

ReactJs Print value instantly

I am new to reactjs and trying to print update value of input field. What I firstly tried was this:
var App = React.createClass({
render() {
return <div>
<h1>Hello, {this.props.name}</h1>
<input type="text" onKeyUp={this.handleChange} />
<p>{this.handleChange}</p>
</div>;
},
handleChange: function(event) {
return event.target.value;
}
});
App = React.createFactory(App);
React.render(
<App name="World" />,
document.getElementById('mount-point'));
But I don't get it why it is not working. Than I tried this: CodePen maybe someone can help me with printing instantly the value of the input field in the <p> element
var App = React.createClass({
getInitialState: function() {
return {
text: "",
};
},
handleChange: function(event) {
this.setState({ text: event.target.value });
},
render() {
return <div>
<h1>Hello, {this.props.name}</h1>
<input type="text" onChange={this.handleChange} />
<p>{this.state.text}</p>
</div>;
},
});
You must store all state of the component in this.state. Use this.setState to update the state. When you update the state, the component is automatically rerendered with the new state.
The content of the paragraph is the current value of the state. onChange is commonly used instead of onKeyUp to handle changes of state in text inputs. handleChange will update the state when the text input changes.

Get value from input and use on the button

I'am creating component with input element and button element.
I need to get the input value and use with button, for example. How can I do that?
Here's my code:
var InputSearch = React.createClass({
getInitialState: function() {
return {
value: 'pics'
}
},
handleChange: function() {
this.setState({
value: event.target.value
});
},
render: function() {
return (
<input type="text" value={this.state.value} onChange={this.handleChange} />
)
}
});
var ButtonSearch = React.createClass({
handleClick: function(event) {
console.log(this.state.value); // here's go the input value
},
render: function() {
return (
<button onClick={this.handleClick}>GO! </button>
)
}
});
var Search = React.createClass({
render: function() {
return (
<div>
<InputSearch />
<ButtonSearch />
</div>
)
}
});
React.render(
<Search />,
document.getElementById('result')
);
One issue here is that you are breaking a good rule - separate smart and dumb components. https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
The way to do this is to have a parent component that holds all the state and functionality of the children and passes all of this down as props...
//Our smart parent
var SearchContainer = React.createClass({
getInitialState : function() {
return {
value : 'pics'
}
},
handleInput : function(event) {
this.setState({value: event.target.value});
},
render : function() {
return (
<div>
<InputSearch value={this.state.value} onChange={this.handleInput} />
<ButtonSearch value={this.state.value} />
</div>
)
}
});
//Our dumb children
var InputSearch = React.createClass({
propTypes : {
onChange : React.PropTypes.func.isRequired,
value : React.PropTypes.string
},
render : function() {
return (
<input type="text" value={this.props.value} onChange={this.props.onChange} />
)
}
});
var ButtonSearch = React.createClass({
propTypes : {
value : React.PropTypes.string
},
handleClick : function() {
console.log(this.props.value); //log value
},
render : function() {
return (
<button onClick={this.handleClick}>GO! </button>
)
}
});
React.render(<Search />, document.getElementById('result'));
Here we pass the handler function down from parent to child so the input doesn't care what happens to the event it fires on change, it just needs to know that it has a prop called onChange that's a function and it invokes that.
The parent (SearchContainer) handles all of that functionality and passes the changed state down to both the button and the input...
hope that helps
Dan
You left out the event in your handleChange.
handleChange: function(event) {
this.setState({
value: event.target.value
});
},
The main architecture of react is the Parent Child / Master Slave principle.
If you want to pass values between components you have to create relations between.
Like for example
You create your master Component with few default states.
var MyMasterComponent = React.createClass({
getInitialState: function(){
...
},
render: function(){
return(
<ChilComponent1 textiwanttopass={this.state.text} />
);
}
});
With that method you are calling the render of another component within a master component. That way you can pass values from states into another component.
In that case you can access the passed text with this.props.textiwanttopass

React.js two-way binding on block elements

I'm trying to bind a value to a div with react so that I can maintain state for that element (eg. on-off) It looks like I should be using LinkedStateMixin, but my experiment below proves that react doesn't support arbitrary attributes for block level elements. Both elements have default values but the div e.target.value returns undefined from its onclick handler whereas the input element value has been properly set. Any idea how to bind data to the div? Thanks!
var Component = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {message: 'Hello!'};
},
render: function () {
var valueLink = this.linkState('message');
var handleClick = function(e) {
console.log(e.target.value);
valueLink.requestChange(e.target.value);
};
return (
<div>
<input type="text" onClick={handleClick} defaultValue={valueLink.value} />
<div onClick={handleClick} defaultValue={valueLink.value}>
{this.state.message}
</div>
</div>
);
}
});
React.render(<Component />, document.body);
http://jsfiddle.net/su8r5Lob/
var Component = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {
message: 'Hello!',
active: false
};
},
inputClick : function(e) {
console.log(e.target.value);
},
toggleActive : function(e){
console.log('div state', this.state.active);
var newState = !this.state.active;
this.setState({active: newState});
},
render: function () {
var cx = React.addons.classSet;
var valueLink = this.linkState('message');
var classes = cx({
'base-class': true,
'element-active': this.state.active
});
return (
<div>
<input type="text" onClick={this.inputClick} defaultValue={valueLink.value} />
<div onClick={this.toggleActive} className={classes}>
{this.state.message}
</div>
</div>
);
}
});
React.render(<Component />, document.body);
http://jsfiddle.net/su8r5Lob/1/
The reason your code does not work is because <div> elements do not have a value property. Only elements that receive user input have it. So when handleClick is called, valueLink.requestChange receives undefined as a parameter.
I've updated your Fiddle a little bit, and now it does support two-way binding for the onChange event.
var Component = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {message: 'Hello!'};
},
render: function () {
var valueLink = this.linkState('message');
var handleClick = function(e) {
console.log(e.target.value);
valueLink.requestChange(e.target.value);
};
return (
<div>
<input type="text" onChange={handleClick} value={valueLink.value} />
<input type="text" onChange={handleClick} value={valueLink.value} />
</div>
);
}
});
React.render(<Component />, document.body);
But, if you want to bind it to a div element, I give you this suggestion. I'm not sure if it is exactly what you expect, but here it is:
var Component = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {message: 'Hello!'};
},
render: function () {
var valueLink = this.linkState('message');
var handleClick = function(e) {
console.log(e.target.value);
valueLink.requestChange(e.target.value);
};
return (
<div>
<input type="text" onChange={handleClick} value={valueLink.value} />
<div onClick={handleClick.bind(this, {target: {value: 'someDivValue'}})} defaultValue={valueLink.value}>
{this.state.message}
</div>
</div>
);
}
});
React.render(<Component />, document.body);
Note that I gave the div a default value that is going to be set to the valueLink everytime the user clicks it. And I had to change the event on the input to onchange so it can update its value when the user types something.

Categories