I am new to React and I try to learn it from the recent official documentation where 'componentWillReceiveProps' no longer in lifecycle.
I have a Children component for NEW Product to post :
import React, { Component } from "react";
export default class NewProduct extends Component {
state = {
title : "",
price : 0
}
handleChange =(e)=>{
this.setState({ [e.target.name]: e.target.value });
}
handleSubmit =()=>{
let formData = {
title : this.state.title,
price : this.state.price
}
console.log(formData)
// axios here with formData
}
render() {
return (
<div>
<input type="text" name="title" value={this.state.title}
onChange={this.handleChange}
/>
<input type="text" name="price" value={this.state.price}
onChange={this.handleChange}
/>
<button onClick={this.handleSubmit}>Submit</button>
</div>
)
}
}
This works OK for creating product on database.
And For EDIT Product, the Parent component load data from database using axios and pass it as props to EditProduct Children. <EditProduct productData={this.state.productData} />
Edit form :
import React, { Component } from "react";
export default class EditProduct extends Component {
state = {
title : "",
price : 0
}
handleChange =(e)=>{
// how to handle change........???
}
handleSubmit =()=>{
let formData = {
title : this.state.title,
price : this.state.price
}
console.log(formData)
// axios here with formData
}
render() {
const data = this.props.productData;
return (
<div>
<input type="text" name="title" value={data.title || ""}
onChange={this.handleChange}
/>
<input type="text" name="price" value={data.price || ""}
onChange={this.handleChange}
/>
<button onClick={this.handleSubmit}>Submit</button>
</div>
)
}
}
My problem is I can not change the state by using handleChange since the value on each inputs no more refer to state.
I have tried to solve this problem by using componentWillReceiveProps and change the states by setState. But this method is no more mentioned inside lifecycle, so I need other solution.
Other solution I have tried was using context API where parent store data to state and my EditProduct component consume and modify that states.
My question is : What is the best valid ways to replace componentWillReceiveProps?
Note : Please do not suggest Redux.
Related
I wish to share this code just in case someone might need to solve such a problem of filtering unwanted characters when doing forms in react. For extras, my code shows how to pass props to components inside Route. For simplicity, I have focused on only these two inputs and omitted other stuff such as the submit button and css data for styling those classNames and ids.
import React, { Component } from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import SignupForm from "./components/SignupForm";
class App extends Component {
constructor() {
super();
this.state = {
firstName: "",
lastName: "",
};
//Binding this to the functions used as they wouldn't just work if not bound
this.changeFirstName = this.changeFirstName.bind(this);
this.changeLastName = this.changeLastName.bind(this);
this.lettersOnly = this.lettersOnly.bind(this);
}
changeFirstName(e) {
this.setState({ firstName: e.target.value });
}
changeLastName(e) {
this.setState({ lastName: e.target.value });
}
// Error handler functions
lettersOnly(nameInput) {//Replacing all characters except a-z by nothing with this function
let regex = /[^a-z]/gi;
nameInput.target.value = nameInput.target.value.replace(regex, "");
}
render() {
return (
<Router>
<div className="App">
<Route
exact
path="/"
comp={SignupForm}
render={() => (
<SignupForm
//SignupForm submit props
changeFirstNameHandler={this.changeFirstName}
firstNameValue={this.state.firstName}
changeLastNameHandler={this.changeLastName}
lastNameValue={this.state.lastName}
// Error handlers
nameCharacterFilter={this.lettersOnly}
/>
)}
/>
)}
/>
</div>
</Router>
);
}
}
export default App;
Below is the signup form, which is the child component in this aspect, and also a function component as opposed to its parent component:
import React from "react";
export default function SignupForm(props) {
return (
<div className="container" id="signupForm">
<h1>Signup Form</h1>
<div className="form-div">
<form>
<input
type="text"
placeholder="First Name"
onChange={props.changeFirstNameHandler}
value={props.firstNameValue}
onKeyUp={props.nameCharacterFilter}
className="form-control formgroup"
/>
<input
type="text"
placeholder="Last Name"
onChange={props.changeLastNameHandler}
value={props.lastNameValue}
onKeyUp={props.nameCharacterFilter}
className="form-control formgroup"
/>
</form>
</div>
</div>
);
}
NB: Welcome to improve this code, if you feel the need!
I think you can improve you're code with this changes:
Use the regex directly in the onChange event
Use only one method to update the values
Here is an example of what I mean: https://codesandbox.io/s/react-playground-forked-vreku?fontsize=14&hidenavigation=1&theme=dark
Regards!
Okay here is an improved code and much more cleaner. However, I have just omitted the React Router part to focus on functionality of the state and functions in this case.
I also want the user to see when they type an unwanted character that it actually typed but then just deleted on key up so I have created an independent function justLettersAndHyphen(nameField) from changeValue(event) that is triggered by onKeyUp.
import React from "react";
import SignupForm from "./SignupForm";
class App extends React.Component {
constructor() {
super();
this.state = {
firstName: "",
lastName: ""
};
this.changeValue = this.changeValue.bind(this);
this.justLettersAndHyphen = this.justLettersAndHyphen.bind(this);
}
changeValue(event) {
this.setState({
[event.target.name]: event.target.value,
});
}
// Error handler functions
justLettersAndHyphen(nameField) {
let regex = /[^a-z-]/gi;
nameField.target.value = nameField.target.value.replace(regex, "");
}
render() {
return (
<SignupForm
firstNameValue={this.state.firstName}
lastNameValue={this.state.lastName}
changeValueHandler={this.changeValue}
nameCharacterFilter={this.justLettersAndHyphen}
/>
);
}
}
export default App;
Child component edited with name property added.
import React from "react";
export default function SignupForm(props) {
return (
<div className="container" id="signupForm">
<h1>Signup Form</h1>
<div className="form-div">
<form>
<input
name="firstName"
type="text"
placeholder="First Name"
onChange={props.changeValueHandler}
onKeyUp={props.nameCharacterFilter}
value={props.firstNameValue}
/>
<input
name="lastName"
type="text"
placeholder="Last Name"
onChange={props.changeValueHandler}
onKeyUp={props.nameCharacterFilter}
value={props.lastNameValue}
/>
</form>
</div>
</div>
);
}
I have two components in two files: Firebase and Recipe. I call in Recipe a function createRecipe from Firebase file.
When I call this.setState({ recipies }) an error occurs. I searched a solution and tried to bind context according to results here.
Firebase file:
class Firebase {
constructor () {
app.initializeApp(config)
// TRIED
this.createRecipe = this.createRecipe.bind(this)
this.auth = app.auth()
this.db = app.database()
}
state = {
recipies: {}
}
createRecipe = recipe => {
const recipies = {...this.state.recipies}
recipies[`recipe-${Date.now()}`] = recipe
this.setState({ recipies })
}
}
export default Firebase
Recipe file:
import { withAuthorization } from '../Session'
import { withFirebase } from '../Firebase'
class Recipe extends Component {
state = {
name: '',
image: '',
ingredients: '',
instructions: ''
}
handleChange = event => {
const { name, value } = event.target
this.setState({ [name]: value })
}
handleSubmit = event => {
event.preventDefault()
const recipe = { ...this.state }
// TRIED
this.props.firebase.createRecipe(recipe)
this.props.firebase.createRecipe(recipe).bind(this)
this.resetForm(recipe)
}
render () {
return (
<div>
<div className='card'>
<form
// TRIED
onSubmit={this.handleSubmit>
onSubmit={() => this.handleSubmit>
onSubmit={this.handleSubmit.bind(this)}>
<input value={this.state.name} type='text' name='nom' onChange={this.handleChange} />
<input value={this.state.image} type='text' name='image' onChange={this.handleChange} />
<textarea value={this.state.ingredients} rows='3' name='ingredients' onChange={this.handleChange} />
<textarea value={this.state.instructions} rows='15' name='instructions' onChange={this.handleChange} />
<button type='submit'>Add recipe</button>
</form>
</div>
</div>
)
}
}
const condition = authUser => !!authUser
export default withAuthorization(condition)(withFirebase(Recipe))
Do you have an idea about what's going wrong ? Many thanks.
class Firebase doesn't extend React.component so it doesn't know what state is. This is expected, extend React.component or use state hooks useState().
You are not using react in your Firebase component
How to fix:
import React, {Component }from 'react';
class Firebase extends Component {
constructor(props){
super(props)
// your code
}
// your code
render(){
return null; // this won't change anything on your UI
}
}
I'm new to React hooks. I'm trying to display a props received from another component in a small alert.
import React, { Fragment } from "react";
import { useAlert } from "react-alert";
const Alert = props => {
const alert = useAlert();
console.log("<< # Alert props.message", props.message); // Diplays "deputy saved"
return (
<Fragment>
<button
className="btn btn-outline-secondary"
onClick={() => {
alert.show(props.message); // Displays the previous props received. Not the one from the console log above.
}}
>
Ajouter
</button>
</Fragment>
);
};
export default Alert;
Here is the component in which the alert is rendered. The message is setstate after being fetched by my Api file. This message contains the res.json from my Node server. For example "deputy created" or "this deputy already exists". The message is then sent to the alert component via an attribute and received as a props.
// AddParty Component
import React from "react";
import { Form, Text, TextArea } from "informed";
import styled from "styled-components";
import Alert from "../../core/admin/Alert";
import "react-datepicker/dist/react-datepicker.css";
import Api from "../../../utils/Api";
class AddParty extends React.Component {
constructor(props) {
super(props);
this.state = {
image: {},
message: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange({ name, value }) {
this.setState({
[name]: value
});
}
async onSubmit(formState) {
console.info("formState", formState);
const { image } = this.state;
const newParty = new FormData();
newParty.append("image", image, image.name);
newParty.append("data", JSON.stringify(formState));
const message = await Api.addParty(newParty);
console.log("message :", message);
this.setState({
message
});
}
render() {
const { message, isSubmitted } = this.state;
console.log("message", message);
return (
<Container className="container">
<Form onSubmit={formState => this.onSubmit(formState)}>
<Label>
Nom du parti:
<Text
field="name"
type="text"
className="form-control form-control-sm"
/>
</Label>
<Label>
Description :
<TextArea
field="description"
className="form-control form-control-sm"
/>
</Label>
<Label>
Photo:
<input
type="file"
onChange={event =>
this.handleChange({
name: "image",
value: event.target.files[0]
})
}
/>
</Label>
<Alert
type="submit"
className="btn btn-outline-secondary"
message={message}
/>
</Form>
</Container>
);
}
}
export default AddParty;
I don't understand why the alert displayed is always the one from the previous props received, not the one showing in the console.log
I solved my problem using another react alert module called react-toastify. https://www.npmjs.com/package/react-toastify
Thanks you to all that helped me
I tried to create a custom input component with inputRef (material ui Input component). Looks like the component reference is working but I'm unable to enter any value in the text field after i set the value attribute. I think it's because of the way i implemented the onchange event. I'm not sure what am i missing. Please help.
Here is the codesandbox url
https://codesandbox.io/s/pjlwqvwrvm
Actually you don't need a onChange prop to for get the changed value..
Just get the value from onchange and set the value in state value.
Another mistake is you are not created the constructor, and gave this.props.value to the value prop. That's it not get updated..
Now I created the constructor and give the this.state.value to the value props.
Now you get your onchanged value in custominput component and your submit function also..
import React from "react";
import { render } from "react-dom";
import { Input } from "material-ui-next";
import trimStart from "lodash/trimStart";
import PropTypes from "prop-types";
const defaultProps = {
state: "",
onChange: () => {} // no need
};
const propTypes = {
state: PropTypes.string,
onChange: PropTypes.func
};
class App extends React.Component {
constructor() {
super();
this.state = {
value:''
}
}
handleSubmit(event) {
event.preventDefault();
console.log("state: " + this.state.value); //shows onChanged value in console
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit.bind(this)}>
<CustomInput
labelText="State"
id="state"
value={this.state.value}
onChange={e=> {
this.setState({value:e.target.value})
}}
/>
</form>
</div>
);
}
}
App.propTypes = propTypes;
App.defaultProps = defaultProps;
class CustomInput extends React.Component {
render() {
const {
classes,
formControlProps,
value,
onChange,
labelText,
id,
labelProps,
inputRef,
inputProps
} = this.props;
return (
<div {...formControlProps}>
{labelText !== undefined ? (
<div htmlFor={id} {...labelProps}>
{labelText}
</div>
) : null}
<Input
classes={{
root: labelText !== undefined ? "" : classes.marginTop
}}
id={id}
value={value} ///////// Fixed ////////
onChange={onChange}
inputRef={inputRef}
{...inputProps}
/>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Here is code in sandbox check it..
https://codesandbox.io/s/84rjk4m8l8
You can either go on inputRef - then your value and onChange event are extra - it is called uncontrolled Component. You can see more about it here: https://reactjs.org/docs/uncontrolled-components.html
Or you can do it with value & onChange event - and work with controlled components, you can find more about controlled components here: https://reactjs.org/docs/forms.html#controlled-components
How to solve it (uncontrollable) with inputRef:
class App extends React.Component {
handleSubmit = (event) => {
event.preventDefault();
console.log("input value: ", this.input.value); // will now show you correct input value
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<CustomInput
labelText="State"
id="state"
inputRef={input => {
this.input = input;
}}
/>
<Button onClick={this.handleSubmit} color='primary'>Submit</Button>
</form>
</div>
);
}
}
Instead of "this.state = input", bind input to something else, because this.state is reserved for local state of React Component, and it won't work with it, not like that.
How to solve it (controllable) with state, value & onChange event:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: props.state || ''
}
}
handleSubmit = (event) =>{
event.preventDefault();
console.log("state: ", this.state.value); // will now show you correct input value
}
handleChange = (event) => {
this.setState({value: event.target.value});
}
render() {
const {value} = this.state;
return (
<div>
<form onSubmit={this.handleSubmit}>
<CustomInput
labelText="State"
id="state"
onChange={this.handleChange}
value={value}
/>
<Button onClick={this.handleSubmit} color='primary'>Submit</Button>
</form>
</div>
);
}
}
Note that I added the constructor and defined the local state for component, and I'm changing value inside of state with this.setState (because state is immutable, and that's the right way to update it).
In both examples, you are able to get input value inside of handleSubmit method, will you work with controllable or uncontrollable components, it's up to you :)
Use value={this.state} or value={this.value}
Here is Your sandbox code Updated
https://codesandbox.io/s/qqk2qoxmlj
In my case, I was mistakenly using state from parent component in child component, since I had declared child component inside parent itself. Moving whole state to child component solved the problem.
New to React, I have a TextField that I want to "bind" a value to it, I want it to change the value if the user enters value into the TextField and update the TextField if the value change through some API call.
Is there a way to do it ?
You can do this using state and onChange. Simple example included below:
<TextField
onChange={(name) => this.setState({name})}
value={this.state.name}
/>
A guide for updating TextInput based on a variable is located in the docs.
The way to do this in React is with state. Here's an example in JSX:
import React from 'react';
export default class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
text: 'default',
text2: 'default'
}
}
onChange(e) {
var obj[e.target.name] = e.target.value
this.setState(obj);
}
render() {
return (
<div>
<input type="text" name="text" value={this.state.text} onChange={this.onChange} />
<input type="text" name="text2" value={this.state.text2} onChange={this.onChange} />
</div>
);
}
}
The same thing with React hook
import React,{useState} from 'react';
export default MyForm = () => {
const [text, setText] = useState('default')
const [text2, setText2] = useState('default')
return (
<div>
<input type="text" name="text" value={text} onChange={(e)=> setText(e.target.value)} />
<input type="text" name="text2" value={text2} onChange={(e)=> setText2(e.target.value)} />
</div>
)
}
Two ways data binding in angular work out of the box by using ngModal directive
NgModal: Creates a FormControl instance from a domain model and binds it to a form control element
import {Component} from '#angular/core';
#Component({
selector: 'example-app',
template: `
<input [(ngModel)]="name" #ctrl="ngModel" required>
<p>Value: {{ name }}</p>
<p>Valid: {{ ctrl.valid }}</p>
<button (click)="setValue()">Set value</button>
`,
})
export class SimpleNgModelComp {
name: string = '';
setValue() { this.name = 'Nancy'; }
}
In ReactJS, we don't have such a built-in option to handle two ways data binding, you need to use the state and add an onChange event to the input.
React Controlled Input allow us to implement data binding in React: we bind a value to a state variable and an onChange event to change the state as the input value changes.
Refer to the below snippet:
class App extends React.Component {
constructor() {
super();
this.state = {name : ''}
}
handleChange = (e) =>{
this.setState({name: e.target.value});
}
render() {
return (
<div>
<input type="text" value={this.state.name} onChange={this.handleChange}/>
<div>{this.state.name}</div>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
This is pretty straightforward using useState React Hook and TextInput
import { useState } from "react";
import { TextInput, Text, View } from "react-native";
const App = () => {
const [text, setText] = useState("hello");
return (
<View>
<TextInput value={text} onChangeText={setText} />
<Text>Text value: {text}</Text>
</View>
);
};
export default App;
See it working here