Pass user input value through props in react.js - javascript

I have 2 component, how do I pass user entered value through onChange to parent component? I'm able to pass the 'trigger' upon onChange, but how to pass the value along?
https://jsfiddle.net/gboaxm30
var InputComp = React.createClass({
render: function() {
return (
<div>
<input type="text" onChange={this.props.newVal} />
</div>
);
}
});
var App = React.createClass({
getInitialState(){
return {
inputVal: 0
}
},
inputChangedHandler(props) {
//set user changed value to inputVal
console.log(props)
},
render() {
return(
<div>
<InputComp newVal={this.inputChangedHandler}/>
<h4>{this.state.inputVal}</h4>
</div>
)
}
})
ReactDOM.render(
<App />,
document.getElementById('container')
);

Call a function on the onChange event of the child component and then access the value of input like e.target.value and then pass it to the parent component like this.props.newVal(e.target.value);
var InputComp = React.createClass({
handleChange(e) {
this.props.newVal(e.target.value);
},
render: function() {
return (
<div>
<input type="text" onChange={this.handleChange} />
</div>
);
}
});
var App = React.createClass({
getInitialState(){
return {
inputVal: 0
}
},
inputChangedHandler(val) {
console.log(val);
this.setState({inputVal: val});
},
render() {
return(
<div>
<InputComp newVal={this.inputChangedHandler}/>
<h4>{this.state.inputVal}</h4>
</div>
)
}
})
ReactDOM.render(
<App />,
document.getElementById('container')
);
JSFIDDLE

I've made a demo for you here: http://codepen.io/PiotrBerebecki/pen/pEAQzV
The idea is to use the so-called controlled input as defined in the React docs: https://facebook.github.io/react/docs/forms.html#controlled-components
var InputComp = React.createClass({
getInitialState() {
return {
userInput: ''
};
},
onChange(event) {
this.setState({
userInput: event.target.value
});
this.props.newVal(event.target.value);
},
render() {
return (
<div>
InputComp
<input type="text"
value={this.state.userInput}
onChange={this.onChange} />
</div>
);
}
});
var App = React.createClass({
getInitialState() {
return {
inputVal: ''
};
},
inputChangedHandler(valueFromChild) {
console.log('valuefromChild:', valueFromChild);
this.setState({
inputVal: valueFromChild
});
},
render() {
return (
<div>
<InputComp newVal={this.inputChangedHandler}/>
<h4>{this.state.inputVal}</h4>
</div>
);
}
})
ReactDOM.render(
<App />,
document.getElementById('container')
);

You should take the value from the event
inputChangedHandler(e) {
//set user changed value to inputVal
console.log(e.target.value)
},

I thinktThis would be helpful for you.
let InputComp = React.createClass({
getInitialState() {
return {
textVal: "",
};
},
handleChange(e) {
this.setState({ textVal: e.target.value });
this.props.newVal(this.state.textVal);
},
render: function () {
return (
<div>
<input
type="text"
onChange={this.handleChange}
value={this.state.textVal}
/>
</div>
);
},
});
var App = React.createClass({
getInitialState() {
return {
inputVal: 0,
};
},
inputChangedHandler(val) {
this.setState({ inputVal: val });
},
render() {
return (
<div>
<InputComp newVal={this.inputChangedHandler} />
<h4>{this.state.inputVal}</h4>
</div>
);
},
});
ReactDOM.render(<App />, document.getElementById("container"));

Related

Unable to render a child component inside main Component using iteration

I have a main component defined as App.js
import "./styles.css";
import { Component } from "react";
import Item from "./components/Item";
class App extends Component {
constructor(props) {
super(props);
this.state = { textInput: "", items: [] };
}
insertItem() {
if (this.state.textInput !== "") {
this.setState((state) => {
const list = state.items.push(state.textInput);
return {
items: list,
textInput: ""
};
});
}
}
deleteItem(index) {
this.setState((state) => {
const list = [...state.items];
list.splice(index, 1);
return {
items: list,
textInput: ""
};
});
}
handleChange(event) {
this.setState({ textInput: event.target.value });
}
render() {
const template = (
<div>
<div>
<input
type="text"
value={this.state.textInput}
onChange={(e) => this.handleChange(e)}
/>
<button onClick={this.insertItem.bind(this)}>Add</button>
</div>
<div>
{this.state.items.map((item, idx) => {
return <Item name={item} removeItem={this.deleteItem.bind(this, idx)} />;
})}
</div>
</div>
);
return template;
}
}
export default App;
and a child Component defined in Item.js
import { Component } from "react";
class Item extends Component {
render() {
return (
<div>
<span>{this.props.name}</span>
<span onClick={this.props.removeItem}>X</span>
</div>
);
}
}
export default Item;
Now my UI looks like
In the above code(App.js) Iam trying to iterate the items and then display the names using the child component. But due to some reason, on entering the text in the input and clicking add its not showing up. Also there are no errors in the console.
Please help, thanks in advance
Edited:
After the recent changes I get this error
You do not need to call this.deleteItem(idx) while passing it to the child.
import "./styles.css";
import { Component } from "react";
import Item from "./components/Item";
class App extends Component {
constructor(props) {
super(props);
this.state = { textInput: "", items: [] };
}
insertItem() {
if (this.state.textInput !== "") {
this.setState((state) => {
const list = state.items.push(state.textInput);
return {
items: list,
textInput: ""
};
});
}
}
deleteItem(index) {
this.setState((state) => {
const list = state.items.splice(index, 1);
return {
items: list,
textInput: ""
};
});
}
handleChange(event) {
this.setState({ textInput: event.target.value });
}
render() {
const template = (
<div>
<div>
<input
type="text"
value={this.state.textInput}
onChange={(e) => this.handleChange(e)}
/>
<button onClick={this.insertItem.bind(this)}>Add</button>
</div>
<div>
{this.state.items.map((item, idx) => {
return <Item name={item} removeItem={this.deleteItem.bind(this, idx)} />;
})}
</div>
</div>
);
return template;
}
}
export default App;
Updating state in react requires a new reference to objects.You're using Array#push. It will not detect your new change and the DOM will not update. You need to return a new reference.
insertItem() {
if (this.textInput === "") {
this.setState((state) => {
// const list = state.items.push(state.textInput);
const list = [...state.items, state.textInput];
return {
list,
textInput: ""
};
});
}
}
In order to track the array, you must add the key attribute:
{this.state.items.map((item, idx) => {
return <Item key={idx} name={item} removeItem={this.deleteItem(idx)} />;
})}
Here I used the index, but it would be better to use some ID of your model.
UPDATE:
I'd move the handler binding in the constructor:
constructor(props) {
super(props);
this.state = { textInput: "", items: [] };
this.insertItem = this.insertItem.bind(this);
}
Then:
<button onClick={this.insertItem}>Add</button>
UPDATE 2:
Okay, it seems you have several mistakes (and I didn't notice them at first glance).
Here is the complete working source (tested):
class App extends Component {
constructor(props) {
super(props);
this.state = { textInput: "", items: [] };
}
insertItem() {
if (this.state.textInput !== "") {
this.setState((state) => {
//const list = state.items.push(state.textInput);
const list = [...state.items, state.textInput];
return {
items: list,
textInput: ""
};
});
}
}
deleteItem(index) {
this.setState((state) => {
const list = [...state.items];
list.splice(index, 1);
return {
items: list,
textInput: ""
};
});
}
handleChange(event) {
this.setState({ textInput: event.target.value });
}
render() {
const template = (
<div>
<div>
<input
type="text"
value={this.state.textInput}
onChange={(e) => this.handleChange(e)}
/>
<button onClick={this.insertItem.bind(this)}>Add</button>
</div>
<div>
{this.state.items.map((item, idx) => {
return <Item key={idx} name={item} removeItem={this.deleteItem.bind(this, idx)} />;
})}
</div>
</div>
);
return template;
}
}
export default App;
I think you should move display of template into return statement rather than inside render
...
render() {
return (
<div>
<div>
<input
type="text"
value={this.state.textInput}
onChange={(e) => this.handleChange(e)}
/>
<button onClick={this.insertItem.bind(this)}>Add</button>
</div>
<div>
{this.state.items.map((item, idx) => {
return <Item name={item} removeItem={this.deleteItem.bind(this, idx)} />;
})}
</div>
</div>
);
}

Why event.target.value is undefined?

I have two components named parent and child.
Child component contains checkbox's and it will send selected checkbox's values to Parent component
Child
class Child extends Component{
state = {
options: [],
selected_options: []
}
handleClose = (e) => {
this.props.add(this.state.selected_options)
}
handleChange = (event) => {
console.log(event.target.name);
if(event.target.checked==true){
event.target.checked=false
this.state.selected_options.slice(this.state.options.indexOf(event.target.value),1)
}
else{
event.target.checked = true
this.state.selected_options.push(event.target.value)
}
}
render() {
return(
<div>
<Grid>
{
this.state.options.map(value => {
return(
<Checkbox onChange={this.handleChange} label={value.name} value={value.name} checked={false} />
)
})
}
<Button color='green' onClick={this.handleClose} inverted>
<Icon name='checkmark' /> Apply
</Button>
</div>
);
}
}
and Parent Component
class Parent extends Component {
constructor(props){
super(props);
this.state = {
selected_options:[],
}
}
addOptions = (options) => {
this.setState({
selected_options: options
})
}
render() {
return(
<div>
<Child selected_options={this.state.selected_options} add={this.addOptions} />
</div>
);
}
}
When a checkbox is selected it must output its corresponding value in the console. but it showing undefined
Directly mutating the state or the event value is not the correct idea
You should be doing it like
class Child extends Component{
state = {
checkedState: []
options: [],
selected_options: []
}
handleClose = (e) => {
this.props.add(this.state.selected_options)
}
handleChange = (index, value) => {
var checkedState = [...this.state.checkedState];
if(checkedState[index] === undefined) {
checkedState.push(true)
}
else {
if(checkedState[index] === true) {
var selected_options=[...this.state.selected_options];
var idx = selected_options.findIndex((obj) => obj.name == value)
selected_options.splice(idx, 1);
checkedState[index] = false
} else {
var selected_options=[...this.state.selected_options];
selected_options.push(value);
}
}
this.setState({checkedState, selected_options})
}
render() {
return(
<div>
<Grid>
{
this.state.options.map((value, index) => {
return(
<Checkbox onChange={() => this.handleChange(index, value.name)} label={value.name} value={value.name} checked={this.state.checkedState[index] || false} />
)
})
}
<Button color='green' onClick={this.handleClose} inverted>
<Icon name='checkmark' /> Apply
</Button>
</div>
);
}
}
and from the Parent render the child and not the Parent
Parent
render() {
return(
<div>
<Child selected_options={this.state.selected_options} add={this.addOptions} />
</div>
);
}

React Js SetState using React Router

I have 2 React Classes:
const PostList = React.createClass({
getInitialState: function () {
return {
status: "loading",
posts: []
}
},
render: function() {
return (
<div id="container">
<ReactRouter.Link to="upload"></ReactRouter.Link>
{this.state.status}
{
this.state.posts.map(function (post) {
return (
<Post post={post} />
);
})
}
</div>
);
}
});
const Upload = React.createClass({
render: function() {
return (
<div className="upload">Upload</div>
);
}
})
const MainContent = React.createClass({
render: function() {
return (
<div>
<Header />
<div id="inner-content">
{this.props.children}
</div>
<Footer />
</div>
);
}
})
const routes = (
<Route path="/" component={MainContent}>
<ReactRouter.IndexRoute component={PostList} />
<Route path="upload" component={Upload} />
</Route>
)
var render = ReactDOM.render(<Router history={History}>{routes}</Router>, document.getElementById("content"));
The question is how can i access the setState function on PostList to render new posts?
Posts will be refreshed through socket.io conenction so when a new posts arrives, i just want to set the state of the PostList to rerender the full html code with he new post(s)
Sorry for bad english...
Add componentDidMount function and set up a socket listener inside it such that when a new message arrives, add it to the current list of posts
const PostList = React.createClass({
getInitialState: function () {
return {
status: "loading",
posts: []
}
},
componentDidMount: function () {
var that = this;
// Be sure to set up data fetching code here
// Set up listener for posts updates here
socket.on('update', function (data) {
// Assuming data contains the post content
that.setState({
posts: that.state.posts.concat(data)
});
});
},
render: function() {
return (
<div id="container">
<ReactRouter.Link to="upload"></ReactRouter.Link>
{this.state.status}
{
this.state.posts.map(function (post) {
return (
<Post post={post} />
);
})
}
</div>
);
}
});

Bind 'this' to NavigationBarRouteMapper non-function

I have a NavBarRouteMapper object that I pass to my navbar. However, in the onpress function of one of the buttons I need to access the state, but I'm not sure how to bind 'this' to the object, since it is a non-function. Relevant code as follows
class app extends Component {
state: {
sideMenuIsOpen: boolean,
};
constructor(props: Object) {
super(props);
this.state = {
sideMenuIsOpen: false,
};
};
static NavigationBarRouteMapper = {
LeftButton(route, navigator, index, navState) {
if (index > 0) {
return (
<SimpleButton
// TODO: Make this work, Menu button needs to go to this
// The problem is here. this.state is undefined
onPress={console.log(this.state)}
customText="Back"
style={styles.navBarLeftButton}
textStyle={styles.navBarButtonText}
/>
);
}
},
RightButton(route, navigator, index, navState) {
// TODO change add button for admins
switch (route.title) {
case "Login":
return null;
default:
return (
<SimpleButton
onPress={() => {
navigator.push({
title: "Profile",
component: Profile,
});
}}
customText="Add"
style={styles.navBarRightButton}
textStyle={styles.navBarButtonText}
/>
);
}
},
Title(route, navigator, index, navState) {
return (
<Text style={styles.navBarTitleText}>{route.title}</Text>
);
},
};
render() {
return (
<SideMenu
menu={<Menu navigate={this.navigate} />}
isOpen={this.state.sideMenuIsOpen}
>
<Navigator
ref="rootNavigator"
initialRoute={{
title: "Login",
component: LoginScene,
navigator: this.refs.rootNavigator,
}}
renderScene = {this.renderScene}
navigationBar={
<Navigator.NavigationBar
// Since this is an object, I can't bind 'this' to it,
// and the documentation calls for it to be passed an object
routeMapper={app.NavigationBarRouteMapper}
style={styles.navBar}
/>
}
/>
</SideMenu>
);
};
}
So you're trying to return the parent's state from the child onClick? If so you can add an onClick which calls the parent onClick that you can pass to the child via props.
var Hello = React.createClass({
getInitialState: function() {
return {test: "testing 1 2 3"};
},
clickMethod: function () {
alert(this.state.test);
},
render: function() {
return <div onClick={this.clickMethod}>Hello {this.props.name}</div>;
}
});
var Child = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
https://jsfiddle.net/reactjs/69z2wepo/
Also if you had a list of components where each component needs to pass a unique piece of data to the parent you can do so using bind.
var Hello = React.createClass({
getInitialState: function() {
return {test: "testing 1 2 3"};
},
clickMethod: function (argument) {
alert(argument);
},
render: function() {
return <div onClick={this.clickMethod.bind(this, "custom argument")}>Hello {this.props.name}</div>;
}
});
var Child = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
https://jsfiddle.net/chrshawkes/64eef3xm/1/

button not working in React

I cannot figure out why my handleDelete function isn't running. When I click 'Delete' nothing at all happens.
In dev tools I see the value for the delete button is correct but onClick with it's attributes doesn't show up at all.
var MainContainer = React.createClass({
getInitialState: function(){
return {
name: 'JK_MNO',
friends: [], //friends is items
text: ''
}
},
handleChange: function(e){
//like the onChange function
this.setState({
text: e.target.value
});
},
handleSubmit: function(e){
e.preventDefault();
if(this.state.text !== '') {
var nextfriend = this.state.friends.concat([{
text: this.state.text, id: Date.now()
}]);
var nextText = '';
this.setState({
friends: nextfriend, text: nextText
});
}
},
handleDelete: function(e){
console.log(this.state.friends);
this.friends.splice (this.props.friend.id);
this.setState({
friends: friends
});
},
render: function(){
return (
<div>
<h3> Name: {this.state.name} </h3>
<ShowList friends={this.state.friends} />
<form onSubmit={this.handleSubmit} >
Enter Friends: <input className="friendInput" onChange={this.handleChange} value={this.state.text} />
</form>
</div>
);
}
});
var ShowList = React.createClass({
render: function() {
var createFriend = function(friend) {
return (
<li key={friend.id}>{friend.text} <button onClick={this.props.handleDelete} value={friend.id}>Delete</button> </li>
);
};
return <ul>{this.props.friends.map(createFriend.bind(this))}</ul>;
}
});
ReactDOM.render(<MainContainer />, document.getElementById('container'));
https://jsfiddle.net/USERALPHA32/bdc9trux/
Very close! You just need to pass your delete function down to ShowList as a prop:
Current:
<ShowList friends={this.state.friends} />
Suggested:
<ShowList friends={this.state.friends} handleDelete={this.handleDelete} />

Categories

Resources