In ReactJS, I'm writing a stateless component;
Since I've read avoiding unnecessary states is best practice.
The component represents an input field which executed a function when the input box contains a value.
export const InputField = (props) => {
const InputFieldContentsChanged = (event) => {
props.onChange(event.target.value);
};
return (
<div data-component="input-field"
className={(props.value !== "" ? "value": "")}>
<input type={props.type} value={props.value} onChange={InputFieldContentsChanged} />
<span className="bar"></span>
<label>{props.label}</label>
</div>
);
};
InputField.PropTypes = {
type: PropTypes.oneOf([ "text", "password" ]).isRequired,
label: PropTypes.string.isRequired,
value: PropTypes.string,
onChange: PropTypes.func.isRequired
}
Now,
I've created another component which just is a sample to test the component above.
This looks like the following:
export const SampleComponent = (props) => {
let componentUsername = "";
const onUsernameChanged = (username) => {
componentUsername = username;
};
return (
<InputField type="text" label="Username" value={componentUsername} onChange={onUsernameChanged} />
);
};
So, I'm binding the value to a custom variable in the component which is changed when the contents of the input field does change.
How does it come that the input field component does not update itself with the new username?
Kind regards,
I'm writing a stateless React component since it's best practice to avoid state when not needed.
In your code you are trying to use your own kind of "state" though, and it's just a variable (componentUsername). But since it's not React state, the component does not re-render upon the change of the variable. React simply doesn't know about the change.
So, either use the usual setState instead of re-assigning the your own "state" variable, or put the logic in the parent component and pass the componentUsername to the SampleComponent via props:
const SampleComponent = props => (
<input type="text" onChange={props.onChange} value={props.value} />
);
class ParentComponent extends React.Component {
constructor() {
super();
this.state = { value: '' };
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(e) {
console.log(e.target.value);
this.setState({ value: e.target.value });
}
render() {
return (
<SampleComponent
value={this.state.value}
onChange={this.handleInputChange}
/>
);
}
}
ReactDOM.render(<ParentComponent />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
The idea of functional components is to not perform any changes to the state or props.
Since there is no trigger to re-render you component you won't see any change.
Change this React.Function to a React.Component.
const InputField = (props) => {
const InputFieldContentsChanged = (event) => {
console.log(event.target.value);
props.onChange(event.target.value);
};
return (
<div data-component="input-field"
className={(props.value !== "" ? "value": "")}>
<input type={props.type} value={props.value} onChange={InputFieldContentsChanged} />
<span className="bar"></span>
<label>{props.label}</label>
</div>
);
};
class SampleComponent extends React.Component {
constructor() {
super();
this.state = { componentUsername : ""};
}
onUsernameChanged = (username) => {
console.log(username);
this.setState({componentUsername: username});
}
render() {
return (
<InputField type="text" label="Username" value={this.state.componentUsername} onChange={this.onUsernameChanged} />
);
}
};
ReactDOM.render(<SampleComponent/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Related
I am learning React.js and I want to use onInput event to change the name, but it doesn't work.
Why is this happening? Do I write the wrong function(OnInputChange)?
Here is my app.js
import React, { Component } from "react";
import UserInput from "./Components/UserInput";
import UserOutput from "./Components/UserOutput";
class App extends Component {
state = {
Username: [{ name: "Jacky" }]
};
OnInputChange = event => {
this.setState({
Username: [{ name: "event.target.value" }]
});
};
render() {
return (
<div>
<UserInput OnInput={this.OnInputChange} />
<UserOutput name={this.state.Username[0].name} />
</div>
);
}
}
export default App;
my UserInput.js:
import React from "react";
const UserInput = () => {
return (
<div>
<input type="text" />
</div>
);
};
export default UserInput;
my UserOutput.js:
import React from "react";
const UserOutput = props => {
return (
<div>
<p>I am {props.name}</p>
<p>I am {props.name}</p>
</div>
);
};
export default UserOutput;
Changes:
1- You are not assigning onChange handler to input element in UserInput component, only passing that handler in props, automatically it will not work.
2- You are updating the value in state in wrong way, it should be name: event.target.value (not string).
Code:
const UserInput = (props) => {
return(
<div>
<input type="text" onChange={props.OnInput}></input>
</div>
);
}
OnInputChange = (event) => {
this.setState({
Username:[
{ name: event.target.value },
],
});
}
Working Code:
class App extends React.Component {
state = {
Username: [{ name: "Jacky" }]
};
OnInputChange = event => {
this.setState({
Username: [{ name: event.target.value }]
});
};
render() {
return (
<div>
<UserInput OnInput={this.OnInputChange} />
<UserOutput name={this.state.Username[0].name} />
</div>
);
}
}
const UserInput = (props) => {
return (
<div>
<input type="text" onChange={props.OnInput} />
</div>
);
};
const UserOutput = props => {
return (
<div>
<p>I am {props.name}</p>
<p>I am {props.name}</p>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='app' />
Your OnInput property is not passed down to your input component. It would have to be:
const UserInput = props => {
return(
<div>
<input type="text" onChange={props.OnInput} />
</div>
);
};
Your handler uses a literal string 'event.target.value', it must read the value:
Username: [{ name: event.target.value }],
Also, there is no need to wrap username in an array and another object, you can just use:
Username: event.target.value,
and access this.state.Username.
and initialize as
state = {
Username: "Jacky"
};
1.) You need to pass the event handler to your UserInput component.
const UserInput = ({ onChange }) => {...}
and then
<UserInput onChange={this.OnInputChange} />
2.) You need to use the passed event handler in your input onChange.
<input onChange={onChange} />
3.) You need to use event.target.value not 'event.target.value'.
React Semantic - TextArea
I have initial value
which is showing in the textArea but its not editable anymore.
Any solution?
codepen example:
[1]: https://codepen.io/as3script/pen/VRepqv?editors=1010
You can use state for this purpose
const {
TextArea,
} = semanticUIReact
class App extends React.Component {
constructor(props){
super(props);
this.state={
value: "initial text which I would like to edit" // set initial state
}
}
onChange(e){
this.setState({ value: e.target.value })
}
render() {
const { value } = this.state;
return (
<div>
<TextArea
rows={4}
style={{'width': '550'}}
onChange={(e) => this.onChange(e)}
value={value} //render changed state
/>
</div>
)
}
}
// ----------------------------------------
// Render to DOM
// ----------------------------------------
const mountNode = document.createElement('div')
document.body.appendChild(mountNode)
ReactDOM.render(<App />, mountNode)
How to clear the materialUI textfield value in react?
Check the below code -
<TextField
hintText=""
ref={(node) => this._toField = node}
onChange={this.changeToText}
floatingLabelText="To*"
floatingLabelFixed={true}
fullWidth={true}
/>
I'm using the raisedButton while pressing it validate the above field. If the field has error then displaying the error message. If not, then we need to clear the input. But how can we clear the input text?
if you are using a stateless functional component then you can use react hooks.
Also make sure you are using inputRef
import React, { useState, useRef } from "react";
let MyFunctional = props => {
let textInput = useRef(null);
return (
<div>
<Button
onClick={() => {
setTimeout(() => {
textInput.current.value = "";
}, 100);
}}
>
Focus TextField
</Button>
<TextField
fullWidth
required
inputRef={textInput}
name="firstName"
type="text"
placeholder="Enter Your First Name"
label="First Name"
/>
</div>
);
};
There is a value property that you have to pass to the TextField component.
check example below:
class SomeComponent extends Component {
state = {value: ''}
resetValue = () => {
this.setState({value: ''});
}
render() {
return (
<div>
<TextField
...
value={this.state.value}
/>
<button onClick={this.resetValue}>Reset</button>
</div>
)
}
}
try this
import { Button, Container, InputBase } from '#material-ui/core'
import React, { useState } from 'react'
const ClearText = ()=> {
const [text , setText] = useState("")
const clearTextField = () => setText("")
return (
<Container>
<InputBase
value={text ? text : ""}
onChange={(e)=>setText(e.target.value)}
/>
<Button onClick={clearTextField} > Clear </Button>
</Container>
)
};
export default ClearText;
You need to, somehow, store the input's value. State seems to be an initial approach in this case. Whenever the text changes, you have to update the state. Same applies when you click the button and click the input's value afterwards:
class App extends React.Component {
constructor() {
super()
this.state = {
value: ''
}
this.handleChange = this.handleChange.bind(this)
this.handleClick = this.handleClick.bind(this)
}
handleChange(event) {
this.setState({ value: event.target.value })
}
handleClick() {
// validation...
this.setState({ value: '' })
}
render() {
return (
<div>
<input type="text" value={this.state.value} onChange={this.handleChange}/>
<button onClick={this.handleClick}>Click-me</button>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I'm using React v16 (latest) and I'm trying to create a general form component, that uses props.children.
export class MyForm extends React.Component {
render() {
return (
<div>
<div className="form">
<h3>{this.props.formName}</h3>
<div>
{React.Children.map(this.props.children, t => {return <span>{t}<br></br></span>;})}
</div>
<input type="button" value={this.props.formName} onClick={this.handleClick}/>
</div>
</div>
)
}
}
I want to create small form that just are able to create a meaningful json object and send it in POST
This is an example of such usage:
<MyForm>
<input type="text" name="a1"></input>
<input type="text" name="a2"></input>
</MyForm>
And I want to have many such small forms. Problem is I want each child (props.children) to have an onChange event -
onChange(event) // name is "a1" or "a2", like in the example aboce
{
var obj = {};
obj[name]=event.target.value;
this.setState(obj);
}
-so that I don't need to manually add onChange for each such child
-I guess another solution is to create a component, but I want the form to be flexible for each kind of sub-element (input text, text area, radio buttons,...) and I just want them all to have similar onChange that will set the name of the component and its value to the state...
I tried adding an onChange property in consturctor and in different hooks, but got:
cannot define property 'onChange', object is not extensible
So when are where (if at all) can I add an onChange dynamically to props.children
This is a great use case for a Higher Order Component. You can use a HOC to wrap and add the onChange prop to any component:
const WithOnChange = WrappedComponent => {
return class extends Component {
onChange = e => {
const obj = {};
obj[name]=e.target.value;
this.setState(obj);
}
render() {
return <WrappedComponent {...this.props} onChange={this.onChange} />
}
}
}
...
import Input from './Input';
class MyForm extends Component {
render() {
return (
<form>
...
<Input type="text" name="a1" />
...
</form>
)
}
}
export default MyForm;
....
import WithOnChange from './WithOnChange';
const Input = (props) => (
<input {...props} />
);
export default WithOnChange(Input);
EDIT:
Another option is to move your children map into a higher order component and then create a custom <Form /> component:
const Form = () => {
return <form>{this.props.children}</form>
};
export default WithOnChange(Form);
const WithOnChange = WrappedComponent => {
return class extends Component {
onChange = e => {
const obj = {};
obj[name] = e.target.value;
this.setState(obj);
}
render () {
const children = React.Children.map(this.props.children, child => {
return React.cloneElement(child, { onChange: this.onChange });
});
return <WrappedComponent {...this.props}>{children}</WrappedComponent>
}
}
}
#user967710, can you please test the following code:
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
}
onChange(event) {
debugger;
var obj = {};
obj.name = event.target.value;
this.setState(obj);
}
<MyForm formName="myForm">
<input type="text" name="a1" onChange={this.onChange}></input>
<input type="text" name="a2" onChange={this.onChange}></input>
</MyForm>
I'm using Higher Order Components to decorate my components.
const HOC = (WrappedComponent) => (props) => {
return (
<span>
<p>HOC Comp</p>
<WrappedComponent {...props}/>
</span>
)
}
I do like this pattern explained here: React Higher Order Components in depth
However I have a problem because the HOC causing React to recreate my component tree instead of updating the tree. This is nicely explained here React Reconciliation. HOC returns an anonymous function whereby React doesn't know it is actually rendering the same component. This is bad for performance and makes my input field lose focus.
How could I use HOC components without React recreating my tree on every render()?
Example code:
class Input extends React.Component {
componentWillMount() {
console.log('input component will mount');
}
componentWillUnmount() {
console.log('input component will unmount');
}
render() {
return (
<span>
<input value={this.props.value} onChange={this.props.onChange}/>
</span>
);
}
}
const HOC = (WrappedComponent) => {
const Help = (props) => {
return (
<span>
<WrappedComponent {...props}/>
<p>{props.help}</p>
</span>
)
};
return Help;
}
class MyComponent extends React.Component {
constructor (props) {
super(props);
this.state = {value : 'start value'}
}
onChange(event) {
this.setState({value : event.target.value});
}
render() {
const Element = HOC(Input);
return (
<span>
<Element
value={this.state.value}
onChange={this.onChange.bind(this)}
/>
</span>
)
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('container')
);
See Fiddle example (see in your browser's console to see the mount and unmount logs from the input component every time you change the input and lose your focus)
You don't have to create Element in the render function. Instead you can create it in the constructor:
class MyComponent extends React.Component {
constructor (props) {
super(props);
this.state = {value : 'start value'};
this.element = HOC(Input);
}
...
And use it in your render function like this:
<span>
<this.element
value={this.state.value}
onChange={this.onChange.bind(this)}
/>
</span>
If needed you can update this.element in componentWillReceiveProps() or componentWillUpdate().
UPDATE: fiddle
This is the way how I extended a functional component with a controlled input element via children and it does not lose focus.
/* import React, { Fragment, useState } from 'react' */
const { Fragment, useState } = React
/* HOC */
const withInput = Base => (props) => {
const [inputValue, setValue] = useState()
return <Base children={
<Fragment>
{props.children}<br />
<input
value={inputValue}
onChange={({ target: { value }}) => setValue(value)}
placeholder="input from hoc" /><br />
<span>inputValue: {inputValue}</span>
</Fragment>
} />
}
/* Component */
const MyComponent = ({children}) => (
<span>
<span>own elements</span><br />
{children}
</span>
)
const MyComponentWithInput = withInput(MyComponent)
ReactDOM.render(<MyComponentWithInput>passed children</MyComponentWithInput>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>