React newb here. I have a pure function that returns a form (presentation component). In this form I need to handle onChange events for those text fields that are controlled. FWIU, I need to this.setState(...) in my onChange event handlers. However due to this being a pure function, I don't have access to this.setState(). Is there a nice way to set the state on these onChange events in a ES2015 function? I'm also using redux if this helps. Example code:
import React, {PropTypes} from 'react'
const ApplicationForm = ({submitHandler, person}) => (
<form onSubmit={e => submitHandler(e)}>
<div>
<label htmlFor="firstName">First Name:</label>
<input type="text" name="firstName" onChange={e => setState(e.target.value)} value={person.firstName || ''}/>
</div>
...
</form>
)
That is a Stateless Function, there is no state to set
If you're using redux, you probably want to trigger a redux action in the onChange, passing the new value as an argument, and have the action update the value of firstName in the redux store for person.firstName
I would recommend taking a look at redux-form to reduce a bunch of boilerplate
You can actually use setState in something that looks like a functional component, but it's pretty hacky. I imagine this method is something only people who really can't stand using the this keyword and class syntax would ever use. Still, I think it's kind of fun.
Here's how you might write an input that changes another element in a normal way using this and class syntax:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {text: "Hello, world"};
}
handleChange = (event) => {
this.setState({text: event.target.value});
}
render() {
return (
<div>
<input value={this.state.text} onChange={this.handleChange} />
<h1>{this.state.text}</h1>
</div>
)
}
}
And here's how you could create the same effect without this and class syntax:
function App() {
"use strict";
const obj = {
state: {text: "Hello, world"},
__proto__: React.Component.prototype
};
obj.handleChange = function (event) {
obj.setState({text: event.target.value});
};
obj.render = function () {
return (
<div>
<input value={obj.state.text} onChange={obj.handleChange} />
<h1>{obj.state.text}</h1>
</div>
);
};
return obj;
}
The trick is to make the App function inherit from React.Component by setting the dunder proto property of the object App returns to React.Component's prototype so that it can use setState.
Here's a codepen if you want to play around with the code.
Stateless functional components can't have state... because they're stateless. If you want to have event handlers to call and state to set, you will need to create a component class, either via React.createClass or by using ES6 classes.
You can use react hooks to achieve what you want.
If you write a function component and you want to add some sate to your function, previously you had to change your function into a class. But now you can use react hooks to create your state in your functional component.
EX:- We write class components with state as below
class Foo extends Component {
constructor(props) {
super(props);
this.state = {
age: 20
};
}
now we can achieve above code in function component as followed
import React, { useState } from 'react';
function Foo() {
const [age, setAge] = useState(20);
Refer this document for more details - https://reactjs.org/docs/hooks-state.html
With React Hooks, we now have state usability extended to functional components as well.
To use this, we can import {useState} from React and pass default value into its arguments.
import React, {PropTypes, useState} from 'react'
const ApplicationForm = ({submitHandler, person}) => (
const [name, updateName]= useState(person.firstName);
<form onSubmit={e => submitHandler(e)}>
<div>`enter code here`
<label htmlFor="firstName">First Name:</label>
<input type="text" name="firstName" onChange={e => updateName(e.target.value)} value={name || ''}/>
</div>
...
</form>
)
More details about this can be found in the documentation for useState.
Related
I want all my components using input[type=text] to get RTL or LTR direction based on user input automatically.
Back in old days(2 or 3 years ago) I used jQuery to select all these inputs and apply my script like this. But what is the best solution to implement this feature in React?
build your wrapper around Input component and do your logic inside this component :) Then everywhere in the code use your <CustomInput /> instead <input ...>.
EDIT:
enclosing a code example of wrapping input element:
import React from "react";
class CustomInput extends React.Component {
render() {
const {onChange, ...otherProps} = this.props;
// Please provide onChange callback to make this Input element "Controlled"
// otherProps are there for things like default value etc. :)
return(
<input type="text" onChange={onChange} />
);
}
}
export default CustomInput;
and if you will not use any of the lifecycle methods you can even implement this component as a function
import React from "react";
const CustomInput = ({onChange, ...otherProps}) => (
<input type="text" onChange={onChange} />;
);
export default CustomInput;
I'm trying to make suggested search entries display in from Google Api appear in the div with the id Suggested-Places using input values from the input tag with the id SearchBar. Unfortunately,the event handlers aren't firing.
here is my code
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg';
import './App.css';
import MdShoppingCart from 'react-icons/lib/md/shopping-cart'
export default class HeaderMin extends Component{
constructor(props) {
super(props);
this.suggestedPlaces=[];
this.state={
suggestions:this.suggestedPlaces
}
this.userLocationInput=this.userLocationInput.bind(this);
this.suggestedLocations=this.suggestedLocations.bind(this);
}
componentDidMount() {
this.address=this.refs.inputBox.value;
const searchBar=ReactDOM.findDOMNode(this.refs.inputBox);
searchBar.addEventListener('keyUp keyPress keyDown',this.userLocationInput)
}
suggestedLocations(location){
this.suggestedPlaces.push(location);
}
userLocationInput() {
const key="&key=AIzaSyCvfy3g8ljGFtVyfCP9idWbwRo_-HASt_0",url="https://maps.googleapis.com/maps/api/place/textsearch/json?query=";
let query=this.address;
const endPoint=url+query+key;
return fetch("http://localhost:8080/"+url+query+key)
.then((res)=>res.json())
.then((res)=>res.results.map((loc)=>this.suggestedLocations(loc.formatted_address)))
}
render(){
return(
<div className="myheader header-min">
<img src="http://res.cloudinary.com/www-mybukka-com/image/upload/v1505151382/logo_m8ik1x.png" id="logo" alt="logo"/>
<div className="search-box search-box-min">
<div>
<input type='text' ref="inputBox" id="SearchBar" defaultValue='search your location'/>
<div id="Suggested-Places">{this.state.suggestions.map((location)=><p>{location}</p>)}</div>
</div>
<button className="btn-sml btn-red"></button>
</div>
<div className="header-top-button header-top-button-min">
<button ></button>
<button className="btn-red"></button>
<MdShoppingCart className="shopping-cart"/>
</div>
</div>
)
}
}
React use synthetic events, so your regular events won't probably work. Use the regular React way if you want it to work.
You should be using Reacts built-in event handler props. Also, you can simplify your code:
<input type='text' ref="inputBox" id="SearchBar" defaultValue='search your location' onKeyDown={this.userLocationInput}/>
You probably don't need to bind the same event to all 3 key events, onKeyDown should be enough. If you need the other ones as well, you can use onKeyPress and onKeyUp.
EDIT:
And after looking at userLocationInput, you'll need to make a small change:
userLocationInput(e) {
const key="&key=AIzaSyCvfy3g8ljGFtVyfCP9idWbwRo_-HASt_0",url="https://maps.googleapis.com/maps/api/place/textsearch/json?query=";
let query = e.target.value; // or this.refs.textInput.value
const endPoint=url+query+key;
return fetch("http://localhost:8080/"+url+query+key)
.then((res)=>res.json())
.then((res)=>res.results.map((loc)=>this.suggestedLocations(loc.formatted_address)))
}
When you bind this.address in the componentDidMount to this.refs.textInput.value, that's a one time assignment. this.address won't update everytime the value gets changed. So instead you should be using e.target.value or this.refs.textInput.value.
One last note, string refs are being deprecated in React so you should be using a ref callback instead.
<input ref={ ref => this.textInput = ref } />
...
// Getting value from input
this.textInput.value;
as the link https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute
It then only gives an example of using the component immediately. I'm trying to find out how i would use this function to access the component immediately, and save the component for future use, as it says we are able to do.
The difference is using ref={callback} react passes the responsibility of managing the reference storage back to you. When you use ref="sometext", under the covers react has to create a refs property on your class and then add all the ref="sometext" statements to it.
While its nice to have a simple this.refs.sometext access to components its difficult and error prone on the react side to clean up this refs property when the component is destroyed. It's much easier for react to pass you the component and let you handle storing it or not.
According to the react docs
React will call the ref callback with the DOM element when the
component mounts, and call it with null when it unmounts.
This is actually a pretty slick idea, by passing null on unmount and calling your callback again you automatically clean up references.
To actually use it all you have to do is access it from any function like so:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.focus = this.focus.bind(this);
}
focus() {
// Explicitly focus the text input using the raw DOM API
this.textInput.focus();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in this.textInput.
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={this.focus}
/>
</div>
);
}
}
The callback you set on ref will receive the component as the first parameter, the 'this' word will be the current class 'CustomTextInput' in this example. Setting this.textInput in your callback will make textInput available to all other functions like focus()
Concrete Example
Tweet from Dan Abermov showing a case where ref callbacks work better
Update
Per Facebook Docs using strings for refs is consider legacy and they "recommend using either the callback pattern or the createRef API instead."
When you assign a ref={callback} like <input type="text" ref={(input) => {this.textInput = input}}/> what basically you are doing is saving the ref with the name textInput for future use. So instead of using ref="myInput" and then using this.refs.myInput we can use the call back bethod and then access the component later like this.textInput.
Here is a demo for the same, whereby we are accessing the input value using ref on button click
class App extends React.Component {
constructor(){
super();
}
handleClick = () => {
console.log(this.textInput.value);
}
render() {
return (
<div>
<input type="text" ref={(input) => {this.textInput = input}}/>
<button type="button" onClick={this.handleClick}>Click</button>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.4/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.4/react-dom.min.js"></script>
<div id="app"></div>
With React 16.3, you can use React.createRef() API instead:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
I'm currently trying to refactor the simple-todos tutorial for meteor using presentational and container components, but ran into a problem trying to access the refs of an input in a functional stateless component. I found out that to access refs, you have to wrap the component in a stateful component, which I did with the input.
// import React ...
import Input from './Input.jsx';
const AppWrapper = (props) => {
// ... more lines of code
<form className="new-task" onSubmit={props.handleSubmit}>
<Input />
</form>
}
import React, { Component } from 'react';
This Input should be stateful because it uses class syntax, at least I think.
export default class Input extends Component {
render() {
return (
<input
type="text"
ref="textInput"
placeholder="Type here to add more todos"
/>
)
}
}
I use refs to search for the input's value in the encompassing AppContainer.
import AppWrapper from '../ui/AppWrapper.js';
handleSubmit = (event) => {
event.preventDefault();
// find the text field via the React ref
console.log(ReactDOM.findDOMNode(this.refs.textInput));
const text = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
...
}
The result of the console.log is null, so is my Input component not stateful? Do I need to set a constructor that sets a value for this.state to make this component stateful, or should I just give up on using functional stateless components when I need to use refs?
or should I just give up on using functional stateless components when I need to use refs?
Yes. If components need to keep references to the elements they render, they are stateful.
Refs can be set with a "callback" function like so:
export default class Input extends Component {
render() {
// the ref is now accessable as this.textInput
alert(this.textInput.value)
return (
<input
type="text"
ref={node => this.textInput = node}
placeholder="Type here to add more todos"
/>
)
}
}
You have to use stateful components when using refs. In your handleSubmit event, you're calling 'this.refs' when the field is in a separate component.
To use refs, you add a ref to where you render AppWrapper, and AppWrapper itself must be stateful.
Here's an example:
AppWrapper - This is your form
class AppWrapper extends React.Component {
render() {
return (
<form
ref={f => this._form = f}
onSubmit={this.props.handleSubmit}>
<Input
name="textInput"
placeholder="Type here to add more todos" />
</form>
);
}
};
Input - This is a reusable textbox component
const Input = (props) => (
<input
type="text"
name={props.name}
className="textbox"
placeholder={props.placeholder}
/>
);
App - This is the container component
class App extends React.Component {
constructor() {
super();
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
const text = this._wrapperComponent._form.textInput.value;
console.log(text);
}
render() {
return (
<AppWrapper
handleSubmit={this.handleSubmit}
ref={r => this._wrapperComponent = r}
/>
);
}
}
http://codepen.io/jzmmm/pen/BzAqbk?editors=0011
As you can see, the Input component is stateless, and AppWrapper is stateful. You can now avoid using ReactDOM.findDOMNode, and directly access textInput. The input must have a name attribute to be referenced.
You could simplify this by moving the <form> tag into the App component. This will eliminate one ref.
I'm using webpack with React and I can't for the life of me understand what is going on in this build. This is what's supposed to be happening.
The var headerInput changes to whatever value is inputted onChange.
When the form is submitted(onSubmit) the console.log logs the headerInput value.
The problem: The value that gets console logged is numerical, it's usually something like: .0.0.1. I think it's console.log'ing the click event. Why isn't the value being assigned like in the in the handlerInput function?
Any help is much appreciated.
Thanks, All.
var headerInput = null;
import React from "react";
export default class Navigation extends React.Component{
handlerInput(e,headerInput){
headerInput = e.target.value;
console.log(headerInput);
};
clickSubmit(e,headerInput){
e.preventDefault();
console.log(headerInput);
};
render(){
return(
<form onSubmit={this.clickSubmit.bind(this)}>
<input type="text" placeholder="change header" onChange={this.handlerInput.bind(this)} />
<button>Change Header</button>
</form>
);
}
};
This is not the recommended way to be using React. Instead of relying on a "global" to store your state, you should be using the state API that come with components.
Like so:
import React from "react";
export default class Navigation extends React.Component{
constructor(props) {
super(props);
// Set up your default state here.
this.state = { };
// Do early binding.
this.handlerInput = this.handlerInput.bind(this);
this.clickSubmit = this.clickSubmit.bind(this);
}
handlerInput(e){
// Use setState to update the state.
this.setState({
headerInput: e.target.value
}
};
clickSubmit(e){
e.preventDefault();
// You read state via this.state
console.log(this.state.headerInput);
};
render(){
return(
<form onSubmit={this.clickSubmit}>
/* Make sure you pass the current state to the input */
<input
type="text"
placeholder="change header"
onChange={this.handlerInput}
value={this.state.headerInput}
/>
<button>Change Header</button>
</form>
);
}
};
I definitely recommend you revisit the official react docs, like the thinking in react and react forms tutorials.
If the input is strictly one-way (you only read from it) then just use a ref
import React from "react";
class Navigation extends React.Component{
clickSubmit(e,headerInput){
e.preventDefault();
console.log(this.inputEl.value);
};
render(){
return(
<form onSubmit={this.clickSubmit.bind(this)}>
<input placeholder="change header" ref={el => this.inputEl = el} />
<button>Change Header</button>
</form>
);
}
};
Note that...
Although string refs are not deprecated, they are considered legacy,
and will likely be deprecated at some point in the future. Callback
refs are preferred.
https://facebook.github.io/react/docs/more-about-refs.html