I'm planning to add a prefilled form with React. I have the actual data on props. This is what I came up with.
#connect(...)
class Some extends React.Component {
state = {
...this.props.auth.user
}
render() {
// Create a form using the data on state
}
}
It looks not correct since I'm not using a react lifecycle hook here. I would like to ask if there is a better practice to achieve what I'm trying to do.
I am not sure about your architecture,since you are using uncontrolled component here, it is recommended to keep the source of truth at one place.
you can do something like this:
#connect(...)
class Some extends React.Component {
constructor(props) {
super(props);
this.state = {
userName:this.props.auth.user
}
}
handleChange = (event) => {
this.setState({userName: event.target.value});
}
render() {
return(
<div>
<input onChange={this.handleChange} id="some" type="text" value= {this.state.userName}/>
</div>
)
}
}
If you want to use controlled component that is controlled through parent/container. you can manage the values through props and set the props onChange.
So to elaborate on my previous responses you would do something like this to achieve what you want:
#connect(...)
class Some extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
}
}
handleChange = (event) => {
this.setState({ value: event.target.value });
}
render() {
return(
<div>
<input onChange={this.handleChange} id="some" type="text" value= {this.state.value|| this.props.value}/>
</div>
)
}
}
While your value is an empty string (in the state), the fields will be populated from your props and as soon as you start typing it will overwrite the prepopulated values with the ones in your state.
Best practices would be to actually have a Component that handles this logic and then passes the props to the form that should be just a dumb presentational component:
class SomeController extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
}
}
handleChange = (event) => {
this.setState({ value: event.target.value });
}
return (<Form handleChange={this.handleChange} value={this.state.value} />)
}
And then your form component:
const Form = (props) => (
<form>
<input onChange={props.handleChange} value={props.value} />
</form>
);
Hope this explanation helps.
Related
By using class, here is a small ref for all of you :
Class ShowValue Extends Component{
constructor(props){
super(props);
this.state = {
name: “ “,
}
}
onChangeHandler = (e) => {
this.setState(
name: e.target.value,
)
}
render(){
return (
<input type=” text” onChange={this.onChangeHandler} value=” My name”>
<p>{this.state.name}</p>
);
}
}
Am I able to create a variable for each input field, and listening on the onChange event and then call the “set” function for that variable by using hooks.
I have changed your code, now if you write in the input text it will be saved in your state name or vice-versa if you write in your code something in state name it will appear in the input.
I recommend you to check some tutorials or even the official doc
import React from 'react';
class ShowValue extends React.Component {
constructor(props) {
super(props);
this.state = {
name: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ name: event.target.value });
}
render() {
return (
<div>
<input
value={this.state.name}
type="text"
onChange={this.handleChange}
/>
<p>{this.state.name}</p>
</div>
);
}
}
export default ShowValue;
Have in mind:
You need to write a parent element to return different child elements otherwise it will give you error. So i've wrapped the input and p element.
You can not use ” for strings in javascript single quotes or double quotes
' or "
Demo: https://stackblitz.com/edit/react-j4a4dv?file=src%2FShowValue.js
Show the value inside the input by setting the value property of input
<input value={this.state.name} type=”text” onChange={this.onChangeHandler} >
I have a parent component housing two children components(AddPersonForm and PeopleList). When I submit a name via the AddPersonForm, I expect it to be rendered in the PeopleList component, but it doesn't.
Here is my AddPersonForm:
class AddPersonForm extends React.Component {
state = {
person: ""
}
handleChange = (e) => this.setState({person: e.target.value});
handleSubmit = (e) => {
if(this.state.person != '') {
this.props.parentMethod(this.state.person);
this.setState({person: ""});
}
e.preventDefault();
}
render() {
return (
<form onSubmit={this. handleSubmit}>
<input type="text" placeholder="Add new contact" onChange={this.handleChange} value={this.state.person} />
<button type="submit">Add</button>
</form>
);
}
My PeopleList component:
class PeopleList extends React.Component {
constructor(props) {
super(props);
const arr = this.props.data;
this.state = {
listItems: arr.map((val, index) => <li key={index}>{val}</li> );
}
}
render() {
return <ul>{this.state.listItems}</ul>;
}
}
Now the parent component, ContactManager:
class ContactManager extends React.Component {
state = {
contacts: this.props.data
}
addPerson = (name) => {
this.setState({contacts: [... this.state.contacts, name]});
render() {
return (
<div>
<AddPersonForm parentMethod={this. addPerson}×/>
<PeopleList data={this.state.contacts} />
</div>
);
Please what I'm I doing wrong, or not doing?
The issue is in your PeopleList component. The state object which renders your list is created in the constructor when the component mounts, but you have no way of updating it when it recieves new values. It will always give you the initial value.
You could introduce a lifecycle method, componentDidUpdate, which would allow you to compare the previous props to the new props when they arrive, and update the state accordingly. I would recommend you not do this for two reasons:
Storing props directly in a components state is not good practice. You are just creating a copy of the state in the component above and that creates opportunities for confusion and stale values when one of them updates. Ideally, each piece of data should live in only one place.
If all PeopleList is doing is rendering your data, then it doesn't need any state at all. It can act as a display component that maps your props in place and doesn't have to worry about updating itself or managing its own data. This would actually make it a good candidate for conversion into a functional component.
class PeopleList extends React.Component {
render() {
return (
<ul>
{this.props.data.map((val, index) => (
<li key={index}>{val}</li>
))}
</ul>
);
}
}
You are initializing PeopleList with props when its created and mounted but then you are not using new values of props for updating it.
To fix your issue use current value of prop when rendering:
class PeopleList extends React.Component {
render() {
return <ul>{ this.props.data.map((val, index) => <li key={index}>{val}</li>) }</ul>;
}
}
I want to add the user to the options array which I have defined inside the state. Problem is whenever I use prevstate.options.concat([username]). it shows this error. While when I just push username using this.state.push(username) to the array no error occurs. But for good practice in React we never alter directly the state variable, that's why I was using prevstate so I want to know why I am getting this error.
As I am getting value of user and this.state.options also showing in the console when I declare inside adduserso I want to know why my setState is not working using prevstate variable?
class Login extends React.Component{
constructor(props){
super(props);
this.addUser = this.addUser.bind(this)
this.State = {
options : ['hello']
}
}
addUser(username){
console.log(username)
console.log(this.State.options)
this.State.options.push(username)
this.setState((prevState)=>{
// console.log(prevState.options)
return {
options : prevState.options.concat([username])
}
})
}
render(){
return(
<div>
<Form addUser = {this.addUser}/>
</div>
);
}
}
class Form extends React.Component{
constructor(props){
super(props);
this.formData = this.formData.bind(this);
}
formData(e){
e.preventDefault();
const username = e.target.elements.user.value.trim();
console.log(username);
this.props.addUser(username);
}
render(){
return(
<div>
<form onSubmit={this.formData}>
Name: <input type="text" name="user"></input>
<input type ="submit"></input>
</form>
</div>
)
}
}
ReactDOM.render(<Login /> , document.getElementById('app'));
actual result would be to concat a user in the options array using setState
Okay I have mocked your component in a sandBox and the following seems to be working. Here is the sandBox: https://codesandbox.io/s/mq3qyvvjkx
You're issue seems to have been using this.State instead of this.state (lower case s) multiple times in your component. And this looks why you were receiving your error.
class Login extends React.Component {
constructor(props) {
super(props);
this.addUser = this.addUser.bind(this);
this.state = {
options: ["hello"]
};
}
addUser(username) {
console.log(username);
console.log(this.state.options);
this.state.options.push(username);
this.setState(prevState => {
// console.log(prevState.options)
return {
options: prevState.options.concat([username])
};
});
}
render() {
return (
<div>
<Form addUser={this.addUser} />
</div>
);
}
}
class Form extends React.Component {
constructor(props) {
super(props);
this.formData = this.formData.bind(this);
}
formData(e) {
e.preventDefault();
const username = e.target.elements.user.value.trim();
console.log(username);
this.props.addUser(username);
}
render() {
return (
<div>
<form onSubmit={this.formData}>
Name: <input type="text" name="user" />
<input type="submit" />
</form>
</div>
);
}
}
The following produces the following console outputs, with no console errors.
Corbuk
Corbuk
["hello", "corbuk"]
Please let me know if this is the expected output?
My Parent Class which holds the overall App state
class App extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: 1,
};
this.handleChange = this.handleChange.bind(this);
};
handleChange(event) {
const target = event.target;
const newValue = target.value;
if( Math.sign(newValue) === 1 ) {
this.setState({
inputValue: newValue
});
}
}
render() {
return (
<EngInput type="number" value={this.state.inputValue} onChange={this.handleChange}/>
);
}
}
My child component to which I am passing the state as props.
class EngInput extends React.Component {
render() {
return (
<input
type="number"
defaultValue={this.props.value}
onChange={this.props.onChange}
/>
);
}
}
The logic I am trying to implement being - The child input component should accept only positive numbers. If its a negative number, the state should not change and the UI should update to the inputValue instead of the newValue.
But, whats happening in the above code is, even though the state does not change to non negative value, the UI still accepts negative value.
How to implement a logic like, if the value is negative, the UI should still show the old positive value from state.
Use value instead of defaultValue to control the input from the parent component.
class EngInput extends React.Component {
render() {
return (
<input
type="number"
value={this.props.value}
onChange={this.props.onChange}
/>
);
}
}
I have a funky set up. I need a multi stage registration form. I have a parent:
class ContactPage extends React.Component {
constructor(props){
super(props);
this.state = {
stage:0,
name:'',
message:'',
email:'',
phone:''
}
this.setName=(e)=>{
this.setState({name:e});
}
this.setMessage=(e)=>{
this.setState({message:e});
}
this.setEmail=(e)=>{
this.setState({email:e});
}
this.setPhone=(e)=>{
this.setState({phone:e});
}
this.nextStage=()=>{
if(this.state.stage < 3){
this.setState({stage:this.state.stage+1})
}
}
this.previousStage=()=>{
if(this.state.stage >= 1){
this.setState({stage:this.state.stage-1})
}
}
this.stage = [
<ContactName onChange={this.setName} />,
<ContactInfo />,
<ContactMessage name={this.state.name} onChange={this.setMessage} />,
<Send />
]
}
render(){
return (
<div>
{this.stage[this.state.stage]}
<button primary style={style.button} onClick={this.previousStage}> Previous </button>
<button primary style={style.button} onClick={this.nextStage}> Next </button>
</div>
This component renders children based on in what stage of registration the user is. I can receive callbacks from children in parent(children do set the state of the parent), but, when passing state.name from parent to child as a prop, the child receives the initial state, which means the name is empty string.
Child component:
class ContactMessage extends React.Component {
constructor(props){
super(props);
this.state ={
message:'',
name:''
}
this.handleChange=(event)=>{
this.props.onChange(event.target.value);
this.setState({message: event.target.value});
}
}
componentWillReceiveProps(props){
this.setState({name:props.name})
}
render(){
return(
<div>
<h1>{this.state.name}</h1>
<form onSubmit={this.handleSubmit}>
<label htmlFor='messageField'>
Message:
<input className='messageField' type="textfield" value={this.state.message}
onChange={this.handleChange} />
</label>
</form>
</div>
UPDATE: I am receiving initial props in the child components, not updated props from parent state. How do I receive new and updated props from parent state?
As I see, in ContactPage you trying to set event (e) as value of state.name instead of setting this.setState({ name: e.target.value }) in this. setName method.
By the way, you don't have to pass everything through constructor.
For example,
class MyComponent extends Component {
state = {
name: ''
}
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
}
render() {
return (
<input name="name" value={this.state.name} onChange={this.handleChange} />
);
}
}
Try extracting the stage array into its own method. Something like:
class ContactPage extends Component {
constructor(props) {
this.state = {
stage: 0,
name: 0
// and so on
}
}
getStage(index) {
let stages = [
<ContactName onChange={this.setName} />,
<ContactInfo />,
<ContactMessage name={this.state.name} onChange={this.setMessage} />,
<Send />
];
return stages[index];
}
render() {
return (
{this.getStage(this.state.stage)}
<AllTheOtherStuff />
)
}
}
3 things I'm noticing here, which may or may not be your problem.
First: Using state the way you are is inherently bug prone. If you are displaying this.state.name, and are setting name from this.props, both in the constructor and componentWillReceiveProps, then just skip the middle man and display this.props.name instead. Trying to maintain a correct state when the value in state is coming from props is really easy to mess up, thus it is better to just use props directly.
Second: I believe your problem is a mixture of 1 and 2 here, but you are declaring your stage variable in your constructor, so it is only going to use what it has available in the constructor. Move this.stage into your render() and it should display the correct state information.
Third: the way you are using componentWillReceiveProps can lead to confusion. It is better to name your variables something that accurately describes what they are, without possible confusion. I would change componentWillReceiveProps(props) to componentWillReceiveProps(nextProps) since nextProps is more explicit about what those props are you are dealing with.