Get value of input and give to parent in React - reactjs

I have the following two components App and it's child Toolbar.
Toolbar contains a form and and input, and I would like to pass the value from the input up to the App component's state -- but only when the use has confirmed it (on submit or enter etc).
My current attempt is not working as I only get a proxy and an event object back (which does makes sense). How would I do this?
I am not using Redux or anything like it.
App.js
import React, {Component} from 'react'
import {render} from 'react-dom'
import {} from './styles/base.scss'
import ImageContainer from './components/Images'
import Toolbar from './components/Toolbar'
export default class App extends Component {
constructor(props) {
super(props);
// http://www.cloudypoint.com/Tutorials/discussion/javascript-how-to-update-parents-state-in-react/
this.setFolderPathHandler = this.setFolderPathHandler.bind(this);
}
setFolderPathHandler(e) {
e.preventDefault();
console.log(arguments);
// this.setState({
// xyz: input
// });
}
render() {
return (
<div>
<Toolbar setFolderPathHandler={this.setFolderPathHandler} />
<ImageContainer />
</div>
)
}
}
Toolbar/index.js
import React, { Component } from 'react';
import path from 'path';
class Toolbar extends Component {
render() {
return (
<nav className="toolbar">
<div className="column">
{/* START used to be it's own component */}
<form onSubmit={this.props.setFolderPathHandler}>
<div className="form-field">
<input type="text" className="folder-path" ref={(input) => this.input = input} />
<button><i className="fa fa-fw fa-2x fa-folder-open" aria-hidden="true"></i></button>
</div>
</form>
{/* END used to be it's own component */}
</div>
<div className="column">
<button><i className="fa fa-fw fa-2x fa-chevron-left" aria-hidden="true"></i></button>
<button><i className="fa fa-fw fa-2x fa-chevron-right" aria-hidden="true"></i></button>
</div>
</nav>
);
}
}
export default Toolbar;
I did have the input as a separate component but it was too confusing to me (I am new to React).

App.js
import React, {Component} from 'react'
import {render} from 'react-dom'
import {} from './styles/base.scss'
import ImageContainer from './components/Images'
import Toolbar from './components/Toolbar'
export default class App extends Component {
constructor(props) {
super(props);
// http://www.cloudypoint.com/Tutorials/discussion/javascript-how-to-update-parents-state-in-react/
this.setFolderPathHandler = this.setFolderPathHandler.bind(this);
}
setFolderPathHandler(inputValue) {
// this.setState({
// xyz: inputValue
// });
}
render() {
return (
<div>
<Toolbar setFolderPathHandler={this.setFolderPathHandler} />
<ImageContainer />
</div>
)
}
}
Toolbar/index.js
import React, { Component } from 'react';
import path from 'path';
class Toolbar extends Component {
constructor(props) {
super(props);
this.submit = this.submit.bind(this);
}
submit(event) {
event.preventDefault();
this.props.setFolderPathHandler(this.input.value);
}
render() {
return (
<nav className="toolbar">
<div className="column">
{/* START used to be it's own component */}
<form onSubmit={this.submit.bind(this)}>
<div className="form-field">
<input type="text" className="folder-path" ref={(input) => this.input = input} />
<button><i className="fa fa-fw fa-2x fa-folder-open" aria-hidden="true"></i></button>
</div>
</form>
{/* END used to be it's own component */}
</div>
<div className="column">
<button><i className="fa fa-fw fa-2x fa-chevron-left" aria-hidden="true"></i></button>
<button><i className="fa fa-fw fa-2x fa-chevron-right" aria-hidden="true"></i></button>
</div>
</nav>
);
}
}
export default Toolbar;

First, if you want to track input value in toolbar, you should save it into state:
class Toolbar extends Component {
constructor(...args) {
super(...args)
this.state = {text: ''};
}
onTextChange(event) {
const text = event.target.value;
this.setState({text});
}
onSubmit(event) {
event.preventDefault();
this.props.setFolderPathHandler(this.state.text);
}
render() {
return (
<nav className="toolbar">
<div className="column">
<form onSubmit={this.onSubmit.bind(this)}>
<div className="form-field">
<input type="text" className="folder-path" value={this.state.text} onChange={this.onTextChange.bind(this)} />
<button><i className="fa fa-fw fa-2x fa-folder-open" aria-hidden="true"></i></button>
</div>
</form>
</div>
...
</nav>
);
}
}
export default Toolbar;
Note that we are keeping internally the value of the input and we are not passing the native events up to the parent controller.
Then in the parent controller:
setFolderPathHandler(input) {
// do something with the input
}
This solution has the problem that you cannot reset the input value from the parent component unless the component is removed from DOM and added again.
This is usually solved by putting the state to the parent component. The parent can then pass text and onChange handlers to the child that contains the <input> or, the parent can create the <input> and pass it to the child component as a property. Consider:
class Toolbar extends Component {
render() {
return (
<nav className="toolbar">
<div className="column">
<form onSubmit={this.props.onSubmit}>
<div className="form-field">
{this.props.textInput}
<button><i className="fa fa-fw fa-2x fa-folder-open" aria-hidden="true"></i></button>
</div>
</form>
</div>
...
</nav>
);
}
}
and App:
export default class App extends Component {
...
render() {
const textInput = (
<input ... value={this.state.text} onChange={this.onTextChange} />
);
return (
<div>
<Toolbar onSubmit={this.onSubmit} textInput={textInput} />
<ImageContainer />
</div>
)
}
}
This allows to separate components that handle the business logic (have state, smart components) and the components that only present things (stateless, dumb components).

Related

React component in component

I'm trying to do a <DropdownLink /> inside a <BootstrapDropdown /> in my code, but I dunno how to.
I've tried to solve the problem with React documentation, but I didn't find anything.
App.js
import React from 'react';
import '../scss/app.scss';
import BootstrapDropdown from './helpers/bootstrapDropdown';
import DropdownLink from './helpers/dropdownLink';
function App() {
return (
<div className="App">
<BootstrapDropdown name={"Dropdown"}>
<DropdownLink name={"Strona główna"} href={"/"} />
</BootstrapDropdown>
</div>
);
}
export default App;
dropdownLink.js
import React from 'react';
class DropdownLink extends React.Component {
render() {
return <a className="dropdown-item" href={this.props.href}>{this.props.name}</a>
}
}
export default DropdownLink;
bootstrapDropdown.js
import React from 'react';
class BootstrapDropdown extends React.Component {
render() {
return <div className="dropdown">
<button className="btn btn-secondary dropdown-toggle" type="button" id="dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{this.props.name}
</button>
<div className="dropdown-menu" aria-labelledby="dropdownMenuButton">
</div>
</div>
}
}
export default BootstrapDropdown;
I want to show a <DropdownLink /> inside a <div className="dropdown-menu"></div> in <BootstrapDropdown />.
You need to explicitly tell where to render the children:
import React from 'react';
class BootstrapDropdown extends React.Component {
render() {
return <div className="dropdown">
<button className="btn btn-secondary dropdown-toggle" type="button" id="dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{this.props.name}
</button>
<div className="dropdown-menu" aria-labelledby="dropdownMenuButton">
{this.props.children}
</div>
</div>
}
}
export default BootstrapDropdown;

React js communication between parent and child component using props send data from shop to prod details component

This is a shop Component which is the parent component in which I have an image on click handler in which I need to send data to a child component called Prod Details.
And the click event is detailMovie so need to pass it to child using props
The child component should should display the selected data from the function
import React from "react";
import { Link, browserHistory } from "react-router";
import { ProdDetails } from "./ProductDetails";
// import ProductsJSON from "../data/products.json";
export class Shop extends React.Component {
// to route to details page from list
onNavigateProdDetails() {
browserHistory.push("/proddetails");
}
constructor(props) {
super(props);
this.state = {
data: [],
selectedData: {}
};
}
//API Call to fetch data
componentDidMount() {
fetch("https://facebook.github.io/react-native/movies.json")
.then(Response => Response.json())
.then(findresponse => {
console.log(findresponse.movies);
this.setState({
data: findresponse.movies
});
});
}
detailMovie(moviedata) {
//this.props.clickedItem = moviedata;
this.selectedMovieList = moviedata;
};
render() {
// find the clicked item and local storage
let selectedMovie;
return (
<div className="shop pad-30">
<h1>Latest Arrivals</h1>
<div className="shop-inner">
<div className="prods-wrap">
{this.state.data.map((moviedata, i) => {
return (
<div
key={moviedata.id}
onClick={() => this.detailMovie(moviedata)}
data-key={moviedata.id}
className="shop-inner-each"
>
<Link to="/proddetails">
<div className="shop-inner-each-img">
<img src={moviedata.image} className="img-fluid" />
</div>
</Link>
<div className="shop-inner-each-details">
<div className="shop-inner-each-details-left">
<Link to="/proddetails">
<h3>{moviedata.title}</h3>
</Link>
<p>$ {moviedata.releaseYear}</p>
</div>
<div className="shop-inner-each-details-right">
<button type="button" className="fi flaticon-like" />
<button
type="button"
className="fi flaticon-shopping-cart"
/>
</div>
</div>
</div>
);
})}
</div>
</div>
</div>
);
}
}
import React from "react";
import { browserHistory, Link } from "react-router";
export class ProdDetails extends React.Component {
onNavigateShop() {
browserHistory.push("/shop");
}
constructor(props) {
super(props);
// copy current list of items
}
render() {
return (
<div className="proddetails">
<div className="proddetails-left">
<img src="" />
</div>
<div className="proddetails-right">
<div className="proddetails-right-inner">
<Link to="/shop" className="btn btn-pagination">
<i className="fi flaticon-left-arrow" /> Back
</Link>
{/* <h2>{this.props.movieData.title}</h2>
<h3>$ {this.props.movieData.releaseYear}</h3> */}
{/* <p>{this.updatedItem.description}</p> */}
<div className="actions">
<button type="button" className="btn btn-prime">
<i className="fi flaticon-like" /> Add to Wishlist
</button>
<button type="button" className="btn btn-prime">
<i className="fi flaticon-shopping-cart" /> Add to Cart
</button>
</div>
</div>
</div>
</div>
);
}
}

How to render another page onclick

I'm using Reactjs. I want to render another page on the same place where main page is render. Below is code for my app.js file.
import React, { Component } from 'react';
import Online from './components/userview';
import Login from './components/login';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.handleLogin = this.handleLogin.bind(this);
this.state = { LoggedIn: false };
}
handleLogin(props) {
this.setState = { LoggedIn: true };
console.log(this.setstate.LoggedIn);
}
render() {
return (
<div className="App">
<div className="header col-lg-12">
<img
src={require('../src/images/logo.png')}
style={{ maxWidth: '80vh', maxHeight: '100vh' }}
/>
</div>
<button
type="button"
class="btn btn-primary"
onClick={this.handleLogin}
>
Sign In
</button>
<button type="button" class="btn btn-info">
Join Live chat
</button>
<Login />
</div>
);
}
}
export default App;
and here is the page which I want to render at the place to app.js.
import React, { Component } from 'react';
export default class Login extends Component {
render() {
return (
<div className="login">
<form>
<div>
<label>Your ID:</label>
<input type="text" ref="id" />
</div>
<br />
<br />
<input type="submit" value="submit" />
</form>
</div>
);
}
}
How to render this page when I click on login button?
In your App render method, you can do :
if(this.state.LoggedIn) return <Login />
Add the below code inside the App component's render method.
render(){
if(this.state.LoggedIn)
return <Login />;
else {
return(
<div className="App">
....
</div>
);
}
}
Check the working code on CodeSandbox

Why is my component not effectively receiving props?

I keep getting a message that the item I'm trying to access via props is undefined. Can you please tell me why it's not working?
Here is where the instance where the props are attempting to be passed... I'm specifically talking about the tellus and yourTrial props.
import React from 'react'
import Info from './components/Info'
import Upsell from '../../../general/components/order/components/Upsell'
import FormTwo from '../../../general/components/forms/FormTwo'
import Footer from '../../../cbd-desktop/components/layout/Footer'
export default class Order extends React.Component {
render() {
return (
<div>
<div>
<div style={styles.header}>
<img style={styles.leaf} src="./resources/desktop-img/leaves_top.png" />
<img style={styles.logo} src="./resources/desktop-img/logo_white.png" />
</div>
<div style={styles.wrapper}>
<div style={styles.leftWrapper}>
<Info />
<Upsell styles={upsellStyles} />
</div>
<FormTwo styles={formStyles} tellus="Tell us where to send" yourTrial="YOUR TRIAL BOTTLE"} />
</div>
</div>
<Footer style={styles.footer}/>
</div>
)
}
}
And here is where I am trying to display these values on the child... towards the top in two h2s
import React from 'react'
import { connect } from 'react-redux'
import { stepTwoSubmit, saveBillingData } from
'../../actions/formStepTwoActions'
import { addReceiptProduct } from '../../actions/receiptActions'
import FormTwoInputs from './components/FormTwoInputsComponent.jsx'
import Throbber from '../throbber/Throbber'
import FormWarning from './components/FormWarningComponent.jsx'
import Button from '../../../cbd-desktop/components/layout/Button'
const mapStateToProps = state => ({
state:state,
config:state.config,
downsellProduct:state.downsell.downsellProduct || {},
receiptProducts:state.receipt.receiptProducts || []
})
const mapDispatchToProps = {
stepTwoSubmit,
saveBillingData,
addReceiptProduct
}
#connect(mapStateToProps, mapDispatchToProps)
export default class FormTwo extends React.Component {
constructor(props) {
super(props)
componentWillReceiveProps(nextProps) {
let formTwoResponse = nextProps.state.stepTwo.formTwoResponse
this.checkOrderStatus(formTwoResponse)
}
componentDidMount() {
this.fillMainOrder()
this.calculateViewers()
this.calculateTimer()
}
render() {
let { state, props, inputs, onInputFocus, saveInputVal, styles } = this,
CustomTag = props.labels ? 'label' : 'span',
{ submitting, formWarning } = state,
{ invalidInputID, text, visible } = formWarning
return (
<div style={styles.formWrapper}>
<p style={styles.yellowBanner}>{this.state.viewers} people viewing this product right now</p>
<div style={styles.formInnerWrapper}>
<div style={styles.headerWrapper}>
<h2 style={styles.header}>{this.props.tellus}</h2>
<h2 style={styles.header}>{this.props.yourTrial}</h2>
</div>
<div style={styles.weAccept}>
<p style={styles.weAcceptText}>We Accept:</p>
<img style ={styles.cardImage} src="resources/desktop-img/cards.png" />
</div>
<form onSubmit={this.submit}>
<FormTwoInputs onInputFocus={onInputFocus} saveInputVal={saveInputVal} CustomTag={CustomTag} styles={styles} />
<FormWarning visible={visible} invalidInputID={invalidInputID} text={text} />
<Button style={styles.button} buttonText="RUSH MY TRIAL" />
</form>
</div>
<img src="resources/desktop-img/secure.png" />
<div style={styles.urgencyWrapper}>
<div style={styles.urgencyTextWrapper}>
<span style={styles.redText}>{this.state.viewers} people are viewing this offer right now -</span>
<span style={styles.blueText}>{this.state.counter}</span>
</div>
<p style={styles.blueText}>Claim Your Bottle Now</p>
</div>
<Throbber throbberText='Confirming your shipment...' showThrobber={submitting} />
</div>
)
}
}

Pass map's argument to function in ReactJS

I am trying to make a todoList by ReactJS. I want to delete an item by its id, but when I console.log(id), it returns undefined. Here is my code
App.js:
import React, { Component } from 'react';
import './App.css';
import Header from './Components/header';
import InputTodo from './Components/todoInput';
class App extends Component {
constructor(props){
super(props);
this.state={
todos:[
{id:0,text:'Make dinner'},
{id:1,text:'Fold the laundary'},
{id:2,text:'Do homework'}
]
}
}
addHere=(text)=>{
this.setState({
todos:this.state.todos.concat([text])
})
}
removeHere=(id)=>{
console.log(id);
// let arr=this.state.todos;
// let index=arr.findIndex((x)=>x.id===id);
// console.log(index);
}
render() {
return (
<div className='todo-wrapper'>
<Header/>
<InputTodo todoText='Type Here...' addTodo={this.addHere}/>
<div>
{this.state.todos.map((value,key)=>{
return (
<div className='row myList' key={key}>
<p className='col-xs-10'> {value.text}-{value.id} </p>
<button className='btn btn-danger pull-right col-xs-2' onClick={this.removeHere(value.id)}>Delete</button>
</div>
)})}
</div>
</div>
);
}
}
export default App;
The following is InputTodo.js:
import React, {Component} from 'react';
import '../App.css';
export default class InputTodo extends Component{
constructor(props){
super(props);
this.state={
todoInput:{
id:2,
text:''
}
}
}
handleSubmit=(e)=>{
if(this.refs.title.value===''){
alert('You must input something');
}
else{
this.state.todoInput={
id:this.state.todoInput.id+1,
text:this.refs.title.value
};
this.setState(this.state);
this.props.addTodo(this.state.todoInput);
this.refs.title.value='';
}
e.preventDefault();
}
render(){
return(
<form className='input-group' onSubmit={this.handleSubmit}>
<input type='text' ref="title" className='form-control'placeholder={this.props.todoText}/>
<span className='input-group-btn'>
<input type='submit' value='Submit' className='btn btn-primary' />
</span>
</form>
);
}
}
While FuzzyTree's answer will work, a cleaner approach would be extracting the todo item's JSX into its own component. This would have the added benefit of not creating a new function for the button's onClick prop every time App's render function gets called.
The component might look like this:
// TodoItem
class TodoItem extends Component {
handleRemove = () => this.props.onRemove(this.props.id)
render() {
return (
<div className='row myList'>
<p className='col-xs-10'> {this.props.text}-{this.props.id} </p>
<button className='btn btn-danger pull-right col-xs-2' onClick={this.handleRemove}> Delete </button>
</div>
)
}
}
// App render
render() {
return (
<div className='todo-wrapper'>
<Header/>
<InputTodo todoText='Type Here...' addTodo={this.addHere}/>
<div>
{this.state.todos.map(({ id, text }, key) =>
<TodoItem key={key} id={id} text={text} onRemove={this.removeHere} />
)}
</div>
</div>
);
}

Resources