I want to make a component with an API like any standard input element, meaning I want to use it like this: <CustomInput value={this.state.custom_input_state} onChange={this.handleChange} />
Here is what I have so far, but I have no idea how to
Make the custom components value changeable from the parent component
after it has been constructed
Make the parent's onChange handler function recieve a change event when the
custom component's value changes
Here is my test setup:
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
foo: 0
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.increment = this.increment.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleSubmit(event) {
alert(this.state.foo);
event.preventDefault();
}
increment() {
this.setState({foo: this.state.foo + 1});
}
render() {
return(
<form onSubmit={this.handleSubmit}>
<div onClick={this.increment}>Increment from parent</div>
<CustomInput name="foo" value={this.state.foo} onChange={this.handleChange}/>
<input type="submit" value="Submit" />
</form>
)
}
}
class CustomInput extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.value,
};
this.increment = this.increment.bind(this);
}
increment() {
this.setState({value: this.state.value + 1});
}
render() {
return(
<React.Fragment>
<div onClick={this.increment}>Increment self</div>
<input name={this.props.name} value={this.state.value}/>
</React.Fragment>
);
}
}
You have to pass all the CustomInput props to the input element. In CustomInput component actually it not recieving the onChange event.
Pass the prop onChange event to input element
Form Component
class Form extends Component {
constructor() {
super();
this.state = {
foo: 'React'
};
}
handleChange = (event) => {
this.setState({
[event.target.name]:event.target.value
})
}
render() {
return (
<div>
<form>
<Custominput name="foo" onChange={this.handleChange} value={this.state.foo} />
</form>
{this.state.foo}
</div>
);
}
}
CustomInput Component
export default class CustomInput extends React.Component{
render(){
return(
<input {...this.props} />
)
}
}
demo link
Related
I am trying the clear the inputs in the function using following code.
import {Typeahead} from 'react-bootstrap-typeahead';
type requestState={
value:string[]
}
class Data extends Component<{},requestState> {
constructor(props){
super(props);
this.state = {
value: [],
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.typeahead.getInstance().clear();
}
render() {
return(
<Typeahead
ref={(ref) => this.typeahead = ref}
id="data"
labelKey="data"
multiple={true}
options={this.state.values}
placeholder="Choose a data.."
onChange={(e) => this.handleChange(e)}
/>
);
}
}
export default Data;
When I try using this code to clear the inputs at once, I am getting following error: "Property 'typeahead' does not exist on type 'Data'".
Could someone help me how to define typeahead property and what changes have to do to get this working.
This is a react ref issue and you just need to define the ref for use first.
Using classical definition:
class Data extends Component<{},requestState> {
constructor(props){
super(props);
this.state = {
value: [],
}
this.handleChange = this.handleChange.bind(this);
this.typeahead = null;
}
handleChange(e) {
this.typeahead.getInstance().clear();
}
render() {
return(
<Typeahead
ref={(ref) => this.typeahead = ref}
id="data"
labelKey="data"
multiple={true}
options={this.state.values}
placeholder="Choose a data.."
onChange={(e) => this.handleChange(e)}
/>
);
}
}
Using React.createRef
class Data extends Component<{},requestState> {
constructor(props){
super(props);
this.state = {
value: [],
}
this.handleChange = this.handleChange.bind(this);
this.typeahead = createRef<Typeahead>();
}
handleChange(e) {
this.typeahead.current.getInstance().clear();
}
render() {
return(
<Typeahead
ref={this.typeahead}
id="data"
labelKey="data"
multiple={true}
options={this.state.values}
placeholder="Choose a data.."
onChange={(e) => this.handleChange(e)}
/>
);
}
}
Refs and the DOM
This Changes worked for me in typescript to clear the inputs using React Bootstrap Typeahead
import React, { Component } from 'react';
import {Typeahead} from 'react-bootstrap-typeahead';
class Data extends Component<{},requestState> {
typeahead = React.createRef<Typeahead>();
constructor(props){
super(props);
this.state = {
value: [],
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.typeahead.current.state.selected= []
}
render() {
return(
<Typeahead
ref={this.typeahead}
id="data"
labelKey="data"
multiple={true}
options={this.state.values}
placeholder="Choose a data.."
onChange={(e) => this.handleChange(e)}
/>
);
}
}
export default Data;
import React from 'react';
import Child from './Child';
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {
firstName: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount(){
let fn = JSON.parse(localStorage.getItem("fir"));
if((localStorage.getItem("fir")!==undefined) && (localStorage.getItem("fir")!==null)){
this.setState({
firstName: fn
})
}
}
handleChange(e){
this.setState({
[e.target.name] :[e.target.value]
})
}
handleSubmit(){
localStorage.setItem("fir", JSON.stringify(this.state.firstName));
alert('submitted');
console.log(this.state.firstName)
}
render(){
return(
<div>
<p> Parent</p>
<Child
firstName={this.state.firstName}
handleChange={this.handleChange}
handleSubmit={this.handleSubmit}
/>
{this.state.firstName}
</div>
)
}
}
export default Parent;
2.
import React from "react";
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
firstName: props.firstName
};
}
render() {
return (
<div>
<p> Child</p>
<input
type="text"
name="firstName"
value={this.state.firstName}
onChange={this.props.handleChange}
/>
<button onClick={this.props.handleSubmit}> submit</button>
</div>
);
}
}
export default Child;
Here i want to update an input field in Child component, But i'm stucked. can anyone help to update that.
Change the below line in child
value={this.state.firstName}
to
value={this.props.firstName}
because firstname is being passed as a prop to child component
You need to pass the value you need to update has props then use that props in the value of your input in child component. I can see you already passing the firstName has a prop, you just to change state to props
<input
type="text"
name="firstName"
value={this.props.firstName}
onChange={this.props.handleChange}
/>
In Reactjs if I have two child components of an App component, can I take input from one component and output it in the other child component?
class App extends Component{
render(
<Input/>
<Output/>
)
}
Input = () => { //input }
Output = () => { //output }
It's simple. Use the same state value for both components in the parent component:
class App extends Component{
constructor(props){
super(props);
this.state = {
value: ''
}
onValueChange = thisonValueChange.bind(this);
}
onValueChange(e) {
e.preventDefault();
setState({
value: e.target.value
});
}
render(){
return(
<Input value={this.state.value} onChange={this.onValueChange} />
<Output value={this.state.value}/>
);
}
}
Input = (props) => {
<input value={props.value} onChange={props.onValueChange}>
}
Output = (props) => <div>{props.value}</div>);
You can handle the data in the App component via state by passing a handler down to the Input component
and then pass the state value down to the Output component like
const Input = props => <input onChange={props.handleChange} />
const Output = props => <span>{props.data}</span>
class App extends Component {
state = {data:""}
handleChange = e => {
this.setState({ data: e.target.value })
}
render() {
return (
<div>
<Input handleChange={this.handleChange} />
<Output data={this.state.data} />
</div>
)
}
}
`export class Parent extends React.Component{
constructor(props){
super(props);
this.state={
name:'',
sendData:false
}
}
render(){
return (<div><input type="text" onChange={(event)=>
{this.setState({
name:event.target.value,
})
})
/>
//sendData can be made true on a button click
{this.state.sendData ?(<div><Child name={this.state.name}
/>
</div>):null}
</div>)}
}`
Display the data of Parent in the child component.
`export class Child extends React.Component{
render(){
return (<div>Name is :{this.props.name}</div>)
}
}`
I have a constructor in my main component:
class App extends Component {
constructor(props){
super(props);
this.state = {
items: []
}
};
render() {
return (
<div className="App">
<ItemList items={this.state.items}/>
<AddItemForm items={this.state.items}/>
</div>
);
}
}
In component AddItemForm I'm adding to array items objects with properties "item_name" that is string and "comment" with data type object. View of component:
class AddItemForm extends React.Component {
constructor(props) {
super(props);
this.state = {
item:{}
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({item:
{
item_name: event.target.value,
comment:{}
}
});
}
handleSubmit(event) {
event.preventDefault();
this.props.items.push(this.state.item);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
<input type="text" item_name={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
export default AddItemForm;
How can I iterate this array to get all item_name values of every object and display them as list in my ItemList component?
This should help.
class App extends Component {
constructor(props){
super(props);
this.state = {
items: []
}
};
addItemToItemsList = (item) => {
const {items=[]} = this.state;
items.push(item);
this.setState({
items : items
});
}
render() {
return (
<div className="App">
<ItemList items={this.state.items}/>
<AddItemForm
items={this.state.items}
addItemToItemsList={this.addItemToItemsList}
/>
</div>
);
}
}
class ItemList extends React.Component {
render () {
const {items} = this.props;
return (
<div>
{items.map((item, index) => {
return (
<div key={index}>item.item_name</div>
)
})}
</div>
);
}
}
class AddItemForm extends React.Component {
constructor(props) {
super(props);
this.state = {
item: {
item_name : '',
comment:{}
}
};
}
handleChange = (event) => {
const new_item = Object.assign({}, this.state.item, {item_name: event.target.value});
this.setState({
item: new_item
});
}
handleSubmit = (event) => {
event.preventDefault();
this.props.addItemToItemsList(this.state.item);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
<input type="text" item_name={this.state.item.item_name} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
export default AddItemForm;
I think, you have an error inside AddItemForm, you should pass onSubmit function from App to AddItemForm and change items through this function:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: []
}
this.handleSubmit = this.handleSubmit.bind(this);
};
handleSubmit(value){
this.setState({
items: this.state.items.concat(value)
})
}
render() {
return (
<div className="App">
<ItemList items={this.state.items} />
<AddItemForm
onSubmit={this.handleSubmit}
items={this.state.items} />
</div>
);
}
}
About main question, one of the way to solve this problem
const ItemList = ({items}) => (
<div>
{items.map( (item, index)=> (
<div key={index}>{item.item_name}</div>
))}
</div>
);
full working example here: https://codesandbox.io/s/7k624nz94q
You can't add to the array directly. You need to pass a callback that will add to the array in your parent component's state. This is a very common pattern when using react.
Here is a skeleton of what you need to do:
In your parent component, you don't need to pass the whole list to your AddItemForm component, just a addItem callback to your child component:
class App extends Component {
constructor(props){
super(props);
this.state = {
items: []
}
this.addItemToList = this.addItemToList.bind(this);
};
render() {
return (
<div className="App">
<ItemList items={this.state.items}/>
<AddItemForm addItemToList={this.addItemToList}/>
</div>
);
}
addItemToList(newValue) {
// Here you add the item to your state
// Always treat your state as immutable, so create a copy then add the item, then set your new State
const newArray = this.state.items.slice(); // clone
newArray .push(newValue); // Add value
this.setState({items: newArray}); // Set the new state
}
}
More info on how to add items to an array in the state here: React.js - What is the best way to add a value to an array in state
Then you use that callback in your child component:
handleSubmit(event) {
event.preventDefault();
// this.props.items.push(this.state.item);
// Here don't mutate the props, instead call the callback to add the item to your parent's component's state
this.props.addItemToList(this.state.item);
}
To display a list of items, you need to use the map function: https://reactjs.org/docs/lists-and-keys.html#rendering-multiple-components
I have three classes in my ReactJS app. Here they are :
class FirstFilter extends React.Component {
constructor() {
super();
this.state = {
val1: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return (
<Label>
<Input type="select" value={this.state.value} onChange={this.props.handleChange}>
<option disabled selected value>Select plox</option>
<option value='Firdt'>First</option>
<option value='Second'>Second</option>
</Input>
</Label>
);
}
}
class SecondFilter extends React.Component {
constructor() {
super();
this.state = {
val2: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.val2});
}
render() {
return (
<Label>
<Input type="search" placeholder="Print somthing" value={this.state.value} onChange={this.props.handleChange}>
</Input>
</Label>
);
}
}
class Main extends React.Component {
constructor() {
super();
this.state = {
val1: '',
val2: ''
};
this.handleChangeVal1 = this.handleChangeVal1.bind(this);
this.handleChangeVal2 = this.handleChangeVal2.bind(this);
}
handleChangeUUID(event) {
this.setState({val1: event.target.value.toLowerCase()});
}
handleChangeOrigin(event) {
this.setState({val2: event.target.value.toLowerCase()});
}
render() {
return (
<div>
<Form inline>
<FormGroup>
<div>
<UuidRow handleChange = {this.handleChangeVal1} />
</div>
<div>
<OriginRow handleChange = {this.handleChangeVal2} />
</div>
</FormGroup>
</Form>
</div>
);
}
}
I want make SearchField from second filter empty when i check whatever in my DropDownList from first filter. When i only change value in my Main it's still leave typed text there. Maybe I doing something wrong and didn't understand basic hierarchy?
You should pass props to your child component, SearchField, when the state of the parent, Main, has been updated by the callback invoked by the DropDownList. The code in your example isn't consistent, so I can't guess what I need to change to help you, but here is a very basic example of how setting state in the parent via a callback event handler passed to one child (text input) can be used to set props on the second child (text area):
https://codesandbox.io/s/BLwl1y6j2
main.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import FirstChild from './FirstChild';
import SecondChild from './SecondChild';
class Main extends Component {
constructor(props) {
super(props);
this.state = {
val1: '',
};
}
// callback to pass as prop to child element text input
handleChangeVal1 = (event) => {
// sets parent state
this.setState({val1: event.target.value.toLowerCase()});
}
render() {
return (
<div>
<FirstChild handleChange = { this.handleChangeVal1 } value={ this.state.val1 }/>
<SecondChild value={ this.state.val1 }/>
</div>
);
}
}
render(<Main />, document.getElementById('root'));
FirstChild.js
import React, {Component } from 'react';
class FirstChild extends Component {
// input calls handleChange callback passed in props to modify parent component state
render() {
console.log(this.props.value);
return (
<input type="text" value={this.props.value} onChange={this.props.handleChange}/>
);
}
}
export default FirstChild;
SecondChild.js
import React, { Component } from 'react';
class SecondChild extends Component {
// render text area with value passed in props
render() {
console.log(this.props.value);
return (
<textarea value={this.props.value} onChange={this.props.handleChange}/>
);
}
}
export default SecondChild;