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;
Related
I have the following component hierarchy:
App
|
|------|
Results Form
So the Results and Form components are children of the App component. I am trying to implement controlled inputs in the Form. I have my state in the App component as I want to display the data from the form inputs in the Results component. I am getting the Warning: A component is changing an uncontrolled input to be controlled error when I don't set initial values for the state and try to use the form. When I set initial values in the state, these values are displayed in the inputs which is not what I want when a user first goes to the form. What is the best solution to this problem of having to set the state to avoid the React Warning but not wanting to display values in the input prior to the user even using the form?
If you get such an error most likely because you don't have a state that handles the form input:
Here's an example:
App.js
import { useState } from "react";
import Form from "./Form";
import Result from "./Result";
export default function App() {
const [input, setInput] = useState("");
return (
<>
<Form input={input} inputHandler={setInput} />
<Result result={input} />
</>
);
}
Form.js
export default function Form({ input, inputHandler }) {
return (
<form>
<input value={input} onChange={(e) => inputHandler(e.target.value)} />
</form>
);
}
Result.js
export default function Result({ result }) {
return <span>Your input is: {result}</span>;
}
This question already has an answer here:
React hook unable to be called inside of class component
(1 answer)
Closed 8 months ago.
I want to use useRef() and componentDidMount() to autofocus an input field on a page. Here's the page code:
import React, { Component, useState, useEffect } from "react";
import styles from '../..//styles/Home.module.css'
interface Props {
}
class Test extends Component<Props> {
nameRef = React.useRef<HTMLInputElement>(null);
componentDidMount() {
this.nameRef.current!.focus();
}
render() {
return (
<main className={styles.main}>
<form action="/account/sign-up" method="post">
<label htmlFor="last">Name:</label>
<input type="text" id="name" name="name" required ref={this.nameRef} />
<label htmlFor="first">Email address:</label>
<input type="text" id="email" name="email" required />
<button type="submit">Submit</button>
</form>
</main>
)
}
}
export default Test
However I get an error at runtime: Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This error is at the nameRef = React.useRef.. line.
You must not write hooks in class-based component. In order to use hooks you must rewrite your component to a functional component.
UPDATED
I misunderstood question and thats why my answer is probably not complete. If you wan to use refs with class based component you should use React.createRef() function.
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;
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.
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.