Editing input fields in react seeding with default - reactjs

I have a bootstrap modal where a user is supposed to edit what he entered in a table. When I set-state using static getDerivedStateFromProps, the user cannot modify the input box, but when I manually initialize the state with some arbitrary data, the user can modify it. What am I doing wrong?
the component is getting its props from redux.
export default class EditItem extends Component {
constructor(props) {
super(props);
this.state = {
name: ""
};
}
handleChange = (event) => {
let { value, name } = event.target;
this.setState({
[name]: value
})
}
static getDerivedStateFromProps(nextProps, prevState){
return {
name: nextProps.itemDetails.name
}
}
render() {
let {
} = this.state;
let {itemDetails} = this.props
return (
<div id="myModal3" className="modal fade">
<div id={uuidv1()} className="modal-dialog">
<div id={uuidv1()} className="modal-content">
<div id={uuidv1()} className="modal-body">
<form id={uuidv1()} className="form-horizontal form-material">
{this.props.cat_name}
<div id={uuidv1()} className="form-group">
<label id={uuidv1()} className="col-md-8">
Item Name
</label>
<div id={uuidv1()} className="col-md-8">
<input
id={uuidv1()}
onChange={this.handleChange}
value={this.state.name}
name="name"
type="text"
placeholder="ex. Burrito"
className="form-control form-control-line"
/>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
);
}
}

Using getDrivesStateToProps is not a good option like that. You have a prop, why don't you use it directly? You can look here for more explanation.
Set your initial state to your prop:
class App extends React.Component {
constructor( props ) {
super( props );
this.state = {
name: this.props.itemDetails.name,
};
}
handleChange = ( event ) => {
const { value, name } = event.target;
this.setState( {
[ name ]: value,
} );
};
render() {
let { } = this.state;
const { itemDetails } = this.props;
console.log( this.state );
return (
<div>
<div>
<div>
<div>
<form>
{this.props.cat_name}
<div>
<label>Item Name</label>
<div>
<input
onChange={this.handleChange}
value={this.state.name}
name="name"
type="text"
placeholder="ex. Burrito"
className="form-control form-control-line"
/>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
);
}
}
const itemDetails = { name: "foo" };
ReactDOM.render(<App itemDetails={itemDetails}/>, 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>
If your prop is coming from an async operation, I don't know maybe you can use something like this just to be in the safe side:
this.state = {
name: ( this.props.itemDetails && this.props.itemDetails.name ) || "",
};
Maybe there is a better way ¯(°_o)/¯

Related

How to get images from e.target.files[0] by input id so that i can set the state of images and then post it via axios

I have been trying to get the files per id and update the state of the images, but I can select images with their id please tell me how to select the file as per id and update the relevant states. Thanks in advance.
export class KycForm extends React.Component {
state = {
adhar_front: undefined,
adhar_back: undefined,
avatar: undefined,
passbook: undefined,
driving_license: undefined,
pan_card: undefined
};
handleImageChange = (e) => {
console.log(e.target.files[0])
}
handleSubmit = (e) => {
e.preventDefault();
console.log(this.state);
console.log(this.state.images)
let form_data = new FormData();
for (let i = 0; i < this.state.images.length; i++) {
form_data.append('this is imput file', this.state.images[i], this.state.images[i].name)
};
console.log("fORM DTATA APPENDED: " + form_data)
this.props.createUserKyc(form_data)
};
render() {
return (
<div className="col-md-6 m-auto">
<div className="card card-body mt-5">
<h2 className="text-center">Complete Your KYC</h2>
<form onSubmit={this.handleSubmit} encType="multipart/form-data">
<div className="form-group">
<label>Adhar Front</label>
<input
accept="image/png, image/jpeg"
className="form-control"
name="adhar_front"
onChange={this.handleImageChange}
required
type="file"
/>
</div>
<button>Upload</button>
</form>
</div>
</div>
);
}
}
First you should add multiple to your input, then you can upload some files and upload them all like what I did below
class App extends React.Component {
render() {
return (
<div className="App">
<KycForm />
</div>
);
}
}
class KycForm extends React.Component {
state = {
adhar_front: undefined,
adhar_back: undefined,
avatar: undefined,
passbook: undefined,
driving_license: undefined,
pan_card: undefined,
images: []
};
handleImageChange = (e) => {
const copy = { ...this.state };
for (var i = 0; i < e.target.files.length; i++) {
copy.images.push(e.target.files[i]);
}
this.setState(copy);
};
handleSubmit = (e) => {
e.preventDefault();
let form_data = new FormData();
form_data.append("images", this.state.images);
console.log("images.length: ", this.state.images.length);
//you can send form_data to server
};
render() {
return (
<div className="col-md-6 m-auto">
<div className="card card-body mt-5">
<h2 className="text-center">Complete Your KYC</h2>
<form onSubmit={this.handleSubmit} encType="multipart/form-data">
<div className="form-group">
<label>Adhar Front</label>
<input
multiple
accept="image/png, image/jpeg"
className="form-control"
name="adhar_front"
onChange={this.handleImageChange}
required
type="file"
/>
</div>
<button>Upload</button>
</form>
</div>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
<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="root"></div>

Render html based on the radio select

I have two radio buttons and need to render a different form for each when the user click the continue button, how can I do this? Here's the code I have so far.
class App extends Component {
constructor() {
super();
this.state = {
name: "typeOfInternet"
};
this.onChangeValue = this.onChangeValue.bind(this);
}
onChangeValue(event) {
console.log(event.target.value);
}
handleChange = () =>
this.setState({ showComponent: !this.state.showComponent });
render() {
return (
<Container>
<section className="section">
<div className="tittle">
<span>
Vamos começar? Escolha a modalidade e preencha seus dados para
continuar.
</span>
</div>
<div className="check-block" onChange={this.onChangeValue}>
<div className="check">
<input
type="radio"
id="residencia"
name="internet"
value="residencial"
/>
<label for="residencial">Internet Residencial</label>
</div>
<div className="check">
<input
type="radio"
id="empresa"
name="internet"
value="empresarial"
/>
<label for="empresarial">Internet Empresarial</label>
</div>
</div>
<div className="continue">
<button
className="btn-continue"
>
Continue
</button>
</div>
</section>
</Container>
)
}
}
export default App ;
The difference between codes are some input fields that "imternet Empresarial" has and "Internet Residencial" does not
There is no need for onChangeValue function because you're not calling it anywhere instead you can attach handleChange function to the <input> tags.
You should use the name which is provided in <input> inside state. So that it will be easier to use in setState.
There's no need to use .bind() also, with the help of arrow functions => you can escape that.
Inside switch, on different cases of the values instead of using <div>...</div>, you can call different components present in separate file. (in order to make the component readable)
Codesandbox Demo
import { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = {
internet: ""
};
}
handleChange = (e) => this.setState({ [e.target.name]: e.target.value });
renderForm = () => {
switch (this.state.internet) {
case "residencial":
return (
<div>
<h1>residencial form</h1>
</div>
);
case "empresarial":
return (
<div>
<h1>empresarial form</h1>
</div>
);
default:
return null;
}
};
render() {
return (
<section className="section">
<div className="tittle">
<span>
Vamos começar? Escolha a modalidade e preencha seus dados para
continuar.
</span>
</div>
<div className="check-block" onChange={this.onChangeValue}>
<div className="check">
<input
type="radio"
id="residencia"
name="internet"
value="residencial"
onChange={this.handleChange}
/>
<label htmlFor="residencial">Internet Residencial</label>
</div>
<div className="check">
<input
type="radio"
id="empresa"
name="internet"
value="empresarial"
onChange={this.handleChange}
/>
<label htmlFor="empresarial">Internet Empresarial</label>
</div>
</div>
{this.renderForm()}
<div className="continue">
<button className="btn-continue">Continue</button>
</div>
</section>
);
}
}
export default App;
First, you need to create your form element to render when radio buttons get toggled. Something like this:
const Form = ({ type }) => {
return type === "residencial" ? <div>Form 1</div> : <div>Form 2</div>;
};
Then keep track of the currently checked radio button and whether or not the continue button has been pressed. Something like this would work (Sandbox):
const Form = ({ type }) => {
return type === "residencial" ? <div>Form 1</div> : <div>Form 2</div>;
};
class App extends Component {
constructor() {
super();
this.state = {
name: "",
step: 1
};
}
onChangeValue = (event) => {
console.log(event.target.value);
this.setState({ name: event.target.value });
};
handleContinue = () => {
this.setState({ step: 2 });
};
render() {
const { step, name } = this.state;
return (
<div>
<section className="section">
<div className="tittle">
<span>
Vamos começar? Escolha a modalidade e preencha seus dados para
continuar.
</span>
</div>
<div className="check-block">
<div className="check">
<input
type="radio"
id="residencia"
name="internet"
value="residencial"
onChange={this.onChangeValue}
/>
<label for="residencial">Internet Residencial</label>
</div>
<div className="check">
<input
type="radio"
id="empresa"
name="internet"
value="empresarial"
onChange={this.onChangeValue}
/>
<label for="empresarial">Internet Empresarial</label>
</div>
</div>
<div className="continue">
<button
disabled={name === ""}
className="btn-continue"
onClick={this.handleContinue}
>
Continue
</button>
<hr />
{step === 2 ? <Form type={name} /> : null}
</div>
</section>
</div>
);
}
}
export default App;
There is also no need to bind your class functions, just use arrow functions, they will make your life much easier.

Why data is not rendered on refresh in react js with asynchronous call?

I am creating edit form.First i have to get data to edit form and i am calling it in componentDidMount().Please see code below.
import React from 'react';
import CompanyForm from './CompanyForm';
import { connect } from 'react-redux';
import { companyActions } from '../../../redux/actions/company-action';
class EditCompanyPage extends React.Component {
constructor(props){
super(props);
};
componentDidMount () {
const { id } = this.props.match.params
const { dispatch } = this.props;
dispatch(companyActions.getCompany(id));
}
render(){
const {editUser } = this.props;
return(
<div>
<h1>Edit Company</h1>
{
editUser && <CompanyForm handleActionParent={this.handleAction} companyDataFP={editUser} />
}
</div>
);
};
}
function mapStateToProps(state) {
const { editUser } = state.companyReducer;
return {
editUser
};
}
const EditCompany = connect(mapStateToProps)(EditCompanyPage);
export default EditCompany;
see code for CompanyForm component below:
import React from 'react';
class CompanyForm extends React.Component {
constructor(props){
super(props);
this.state = {
company :{
name : this.props.companyDataFP.name || '',
address1 : this.props.companyDataFP.address1 || '',
}
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
};
handleChange(e) {
const { name, value } = e.target;
const newState = Object.assign({}, this.state);
newState.company[name] = value;
this.setState(newState);
}
handleSubmit(e) {
e.preventDefault();
return false;
}
render(){
return(
<div className="col-md-12">
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="col-md-6">
<div className='form-group'>
<label htmlFor="name">Name</label>
<input type="text" name="name" className="form-control" onChange={this.handleChange} value={this.state.company.name} />
</div>
</div>
<div className="col-md-6">
<div className='form-group'>
<label htmlFor="address1">Address 1</label>
<input type="text" name="address1" className="form-control" onChange={this.handleChange} value={this.state.company.address1} />
</div>
</div>
</div>
<div className="row">
<div className="col-md-12">
<div className='form-group'>
<input type="submit" className="btn btn-info" value="submit" />
</div>
</div>
</div>
</form>
</div>
);
};
}
export default CompanyForm;
It works fine when i access this form with
<Link to="/edit-form/:id" >Edit</Link>
but when i refresh the current page then values are not rendering into form to edit.
I am using redux approach for state management, please guide me i am new to react.
Probably ComponyForm initializes form on its componentDidMount lifecycle function, so when editUser arrives nothing will change.
A way to handle this is changing:
<CompanyForm handleActionParent={this.handleAction} companyDataFP={editUser} />
to:
{editUser.name && <CompanyForm handleActionParent={this.handleAction} companyDataFP={editUser} />}

How to accept and pass two parameter as props

Hi I need to pass two parameters, to the class Chat. Currently it is getting only one parameter and displaying correctly.
const Chat = props => (
<div >
<ul>{props.messages.map(message => <li key={message}>{message}</li>)}</ul>
</div>
);
This Chat.js file is called from the Home.js. Suppose I need to pass the Chat component two parameters and I tried it like following.
import React, { Component } from 'react';
import { User } from './User';
import Chat from './Chat';
export class Home extends Component {
displayName = Home.name
state = {
messages: [],
names: []
};
handleSubmit = (message,name) =>
this.setState(currentState => ({
messages: [...currentState.messages, message],
names: [...currentState.names,name]
}));
render() {
return (
<div>
<div>
<User onSubmit={this.handleSubmit} />
</div>
<div>
<Chat messages={this.state.messages,this.state.name} />
</div>
</div>
);
}
}
In this scenario how should I change the Chat component to accept two parameters and display inside div tags.
This is what I tried. But seems it is incorrect.
const Chat = props => (
<div >
<ul>{props.messages.map((message, name) => <li key={message}>{message}</li> <li key={name}>{name}</li>)}</ul>
</div>
);
PS: The User Method
import * as React from 'react';
export class User extends React.Component{
constructor(props) {
super(props);
this.state = {
name: '',
message: ''
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
render() {
return (
<div className="panel panel-default" id="frame1" onSubmit={this.handleSubmit}>
<form className="form-horizontal" action="/action_page.php" >
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="name">Your Name </label>
<div className="col-sm-10">
<input type="text" className="form-control" name="name" placeholder="Enter your Name" onChange={this.handleChange} />
</div>
</div>
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="message">Message</label>
<div className="col-sm-10">
<input type="text" className="form-control" name="message" placeholder="Enter your Message" onChange={this.handleChange}/>
</div>
</div>
<div className="form-group">
<div className="col-sm-offset-2 col-sm-10">
<button type="submit" id="submit" className="btn btn-default">Submit</button>
</div>
</div>
</form>
</div>
);
}
handleChange(evt) {
this.setState({ [evt.target.name]: evt.target.value });
}
handleSubmit = (e) => {
e.preventDefault();
this.props.onSubmit(this.state.message, this.state.name);
this.setState({ message: "" });
this.setState({name:""});
};
}
You can do this by using separate attributes to pass different props. So for instance, you might revise your <Home/> components render method like so:
<Chat messages={this.state.messages} names={this.state.names} />
and then to access these two bits of data (messages and name) from inside the <Chat /> component you could do the following:
const Chat = props => (
<div >
<ul>{props.messages.map((message, index) => <li key={message}>
From: { Array.isArray(props.names) ? props.names[index] : '-' }
Message: {message}</li>)}
</ul>
</div>
);
Hope this helps!
You have to pass them separately:
<Chat messages={this.state.messages} name={this.state.name} />

react add many items in component

I have a component that accepts another component with 3 fields, and I want that I can add new entries to different fields
  now I can add only the entry in firstname and I do not know how to make that for for the lastname and telegrams
if I just copy them, then the values are accepted only from one field
import React, { Component } from 'react';
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
dataItems: []
}
}
addItem(value) {
let newListItems = this.state.dataItems.slice();
newListItems.push(value);
this.setState({
dataItems : newListItems
});
}
render() {
return (
<div>
{this.state.dataItems.map(function (item,index) {
return (
<Hello key={index} firstname={item} lastname={item2} telegram={item3}/>
);
}, this)}
<AddItem addItem={this.addItem.bind(this)} />
</div>
)
}
}
class Hello extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div className='form__row'>
<p className='form__input' > firstname: {this.props.firstname} </p>
<p className='form__input'> lastname: {this.props.lastname} </p>
<p className='form__input'> telegram: {this.props.telegram} </p>
</div>;
}
}
class AddItem extends React.Component{
handleClick(){
this.props.addItem(this.item.value);
}
render(){
return (
<div className='form__row'>
<div>
<label >firstname</label>
<input className='form__input' type="text" ref={item => this.item=item} />
<label >lastname</label>
<input className='form__input' type="text" ref={item2 => this.item=item2} />
<label >telegram</label>
<input className='form__input' type="text" ref={item3 => this.item=item3} />
</div>
<button onClick={this.handleClick.bind(this)}> add new in state</button>
</div>
);
}
}
export default Table;
There are a couple important things you got wrong. I'll break them down for you:
In your AddItem component, you are using an uncontrolled component, but overriding the same property this.item over and over again whenever setting the values for each form field. In this case, because it is the last one, telegrams value is the final one remaining. So it is the only one being set. It would be the same thing as trying to set three different value for the same variable.
item.value = 1
item.value = 2
item.value = 3
If you used this code snippet, obviously the only value of item will be 3. So we first fix this component by associating different properties to each form:
// AddItem component's render function
render(){
return (
<div className='form__row'>
<div>
<label >firstname</label>
<input className='form__input' type="text" ref={item => this.item=item} />
<label >lastname</label>
<input className='form__input' type="text" ref={item2 => this.item2=item2} />
<label >telegram</label>
<input className='form__input' type="text" ref={item3 => this.item3=item3} />
</div>
<button onClick={this.handleClick.bind(this)}> add new in state</button>
</div>
);
}
On another note, I highly recommend actually giving your three items meaningful names. So I would switch them to firstName, lastName and telegram specifically.
Your handleClick also suffers from the same problem: You were passing the same this.item.value, meaning you were just passing your last set field. For you to pass all three of them while keeping the function in it's same format, you need to create an object with all three of your item.value. With that change, it should now look like this:
handleClick(){
const value = {item: this.item.value, item2: this.item2.value, item3: this.item3.value}
this.props.addItem(value);
}
Finally, in your Table component, some changes are needed in its render function. It looks like you are confusing the item you named in your map function with item you passed in. Again, you should probably find a more meaningly names for your variables so that doesn't happen.
The item in the map function, is each item of the array you are mapping through. In this case, it would be just the object with all three of your fields. You actually want the item fields from the item of the map function.
{this.state.dataItems.map(function (item,index) {
return (
<Hello key={index} firstname={item.item} lastname={item.item2} telegram={item.item3}/>
);
}, this)}
Here is a working version of your app:
class Table extends React.Component{
constructor (props) {
super(props);
this.state = {
data: [],
dataItems: []
}
}
addItem(value) {
let newListItems = this.state.dataItems.slice();
console.warn(newListItems)
newListItems.push(value);
this.setState({
dataItems : newListItems
});
}
render() {
return (
<div>
{this.state.dataItems.map(function (item,index) {
return (
<Hello key={index} firstname={item.item} lastname={item.item2} telegram={item.item3}/>
);
}, this)}
<AddItem addItem={this.addItem.bind(this)} />
</div>
)
}
}
class Hello extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div className='form__row'>
<p className='form__input' > firstname: {this.props.firstname} </p>
<p className='form__input'> lastname: {this.props.lastname} </p>
<p className='form__input'> telegram: {this.props.telegram} </p>
</div>;
}
}
class AddItem extends React.Component{
handleClick(){
const value = {item: this.item.value, item2: this.item2.value, item3: this.item3.value}
this.props.addItem(value);
}
render(){
return (
<div className='form__row'>
<div>
<label >firstname</label>
<input className='form__input' type="text" ref={item => this.item=item} />
<label >lastname</label>
<input className='form__input' type="text" ref={item2 => this.item2=item2} />
<label >telegram</label>
<input className='form__input' type="text" ref={item3 => this.item3=item3} />
</div>
<button onClick={this.handleClick.bind(this)}> add new in state</button>
</div>
);
}
}
ReactDOM.render(
<Table />,
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>

Resources