RCohen
RCohen

Reputation: 2002

OnChange not saving the written input value on setState

This seems a silly question, so I apologize in advance.

I have a page with several inputs and at the moment, for some reason, I can't just get the new value written in the input, so I can update the state and send this new data. Seems to me there is something wrong in with the onChange function, because I can get the previous state, but not the new value i'm saving on state.

EDIT: The submit button is outside the input form.

Here is the code:

constructor(props) {
    super(props)

    this.state = {
        editMode: false,
        data: {
            designation: '', 
            address: '', 
            description: ''
        }
    }
}

componentDidMount = () => {
    const dataInfo = data.get('url here');//fetching data here

    const data = {
        designation: dataInfo.designation , 
        address: dataInfo.address, 
        description: dataInfo.description 
    }

    this.setState({
        data: data
    })
}

handleInput = (e) => {
let value = e.target.value;
let name = e.target.name;

this.setState(
  prevState => ({
    data: {
      ...prevState.data,
      [name]: value
    }
  })
);
}

handleFormSubmit = (e) => {
    e.preventDefault();

    const { data } = this.state;

    console.log('hey', data.designation);

    this.setState({
        editMode: false
    })
 }

   render() {

   {!this.state.editMode 
? <button onClick={() => this.setState({ editMode: true })}>edit</button> 
: <div>
   <button 
   className={styles.buttonMargin} 
   onClick={() => this.setState({ editMode: false })}>cancel</button>
   <button onClick={this.handleFormSubmit}>Guardar</button>
  </div> 
    }

    <div>
      {this.state.editMode 
    ? <form onSubmit={this.handleFormSubmit}>
      <input 
        type='text'
        placeholder='Nome do Campo' 
        name='designation' 
        onChange={this.handleInput} 
        defaultValue={this.state.data.designation}
      />
      </form>
    : <p> {this.state.data.designation} </p> }
    </div>
 }
}

Upvotes: 1

Views: 1762

Answers (2)

CWSites
CWSites

Reputation: 1821

There are a number of changes that I would recommend, I have a working version of your code here: https://stackblitz.com/edit/react-vmeuxc

Binding your functions inside of constructor will allow you to write unit tests later and also access the functions from outside of your component if needed.

In my experience, using e.currentTarget.value is more stable than using e.target.value Difference between e.target and e.currentTarget

I didn't see why you were assigning this.state to a constant in handleFormSubmit so I removed that.

You were missing some markup, such as a submit button on your form and an edit button when you weren't in the edit mode. I also don't understand why you had the random this.setState({ editMode: false }) at the end of your render statement, so I removed that since it wasn't necessary.

Having a helper function such as this would help to examine the existing value to the new one.

  compareData(currentData, newData) {
    if(currentData === 'undefined' || (newData && currentData !== newData)) {
      return newData;
    } else {
      return false;
    }
  }

Here's the fully cleaned up version of your code. Note: I had to create a dummy dataInfo object since I don't have access to your API.

  constructor(props) {
    super(props);
    this.compareData = this.compareData.bind(this);
    this.handleInput = this.handleInput.bind(this);
    this.handleFormSubmit = this.handleFormSubmit.bind(this);
    this.state = {
      editMode: false,
      data: {
        designation: '', 
        address: '', 
        description: ''
      }
    }
  }

  componentDidMount = () => {
    const dataInfo = {
      designation: 'test designation',
      address: 'test address',
      description: 'test description'
    };
    // const dataInfo = data.get('url here'); //fetching data here
    const data = {
      designation: dataInfo.designation , 
      address: dataInfo.address, 
      description: dataInfo.description 
    }

    this.setState({
        data: data
    })
  }

  compareData(currentData, newData) {
    if(currentData === 'undefined' || (newData && currentData !== newData)) {
      return newData;
    } else {
      return false;
    }
  }

  handleInput(e) {
    let value = e.currentTarget.value;
    let name = e.currentTarget.name;

    if(this.compareData(this.state.data[name], value)) {
      this.setState({
        data: {
          ...this.state.data,
          [name]: value
        }
      });
    }

    console.log(this.state);
  }

  handleFormSubmit(e) {
    e.preventDefault();

    this.setState({
      editMode: false
    });
  }

  render() {
    return (
      <div>
        {!this.state.editMode ? (
          <button onClick={() => this.setState({ editMode: true })}>edit</button> 
        ) : (
          <div>
            <button onClick={() => this.setState({ editMode: false })}>cancel</button>
            <button onClick={this.handleFormSubmit}>Guardar</button>
          </div> 
        )}

        {this.state.editMode ? (
          <form>
            <input 
              type='text'
              placeholder='Nome do Campo' 
              name='designation' 
              onChange={this.handleInput} 
              defaultValue={this.state.data.designation}
            />
          </form>
        ) : (
          <p> {this.state.data.designation} </p>
        )}
      </div>
    )
  }

Upvotes: 1

Oscar Calderon
Oscar Calderon

Reputation: 999

i tweaked your code a little bit. In my case is working, although i suggest you to use value instead of defaultValue for setting the value content of the input component. I had to declare a dummy object instead of fetching the data, and declare your component as a standard HTML field. Also i added a button to enable edit mode since i don't get in which moment you do that. You can test it in codesandbox io (link to working example is https://codesandbox.io/s/n4lpz784wj):

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      editMode: false,
      data: {
        designation: '',
        address: '',
        description: ''
      }
    }
  }

  componentDidMount = () => {
    //const dataInfo = data.get('url here');//fetching data here
    const dataInfo = {
      designation: "Do something else",
      address: "5th Avenue #45",
      description: "This is a test description"
    }
    const data = {
      designation: dataInfo.designation,
      address: dataInfo.address,
      description: dataInfo.description
    }

    this.setState({
      data: data
    })
  }

  handleInput = (e) => {
    let value = e.target.value;
    let name = e.target.name;

    this.setState(
      prevState => ({
        data: {
          ...prevState.data,
          [name]: value
        }
      })
    );
  }

  toggleEditionMode = () => {
    this.setState({ editMode: true});
  }

  handleFormSubmit = (e) => {
    e.preventDefault();

    const { data } = this.state;

    console.log('hey', data.designation);

    this.setState({
      editMode: false
    })
  }

  render() {
    console.log(this.state.data.designation);
    return (
    <div>
      {this.state.editMode
        ? <form onSubmit={this.handleFormSubmit}>
          <input
            type='text'
            placeholder='Nome do Campo'
            name='designation'
            onChange={this.handleInput}
            defaultValue={this.state.data.designation}
          />

        </form>
          : <p> {this.state.data.designation} <button type="button" onClick={this.toggleEditionMode}>Enable edit</button> </p>}
    </div>
    );
  }

}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Upvotes: 0

Related Questions