2

I have a table with several components and when changing the value of the selected input, I would like to save the new values in the parent state with something like below(so I can submit like this):

monday: {startTime: '08:00', endTime: '23:00'}

Is it possible to do this with one onChange function? If not, how to do this with more than one handling function? Remember, this is to be done with all the days of the week.

Live example here

Parent component:

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

    this.state = {
      editMode: false,
      friday: { endTime: "", startTime: "" },
      monday: { endTime: "", startTime: "" },
      thursday: { endTime: "", startTime: "" },
      tuesday: { endTime: "", startTime: "" },
      wednesday: { endTime: "", startTime: "" },
      saturday: { endTime: "", startTime: "" },
      sunday: { endTime: "", startTime: "" }
    };
  }

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

    this.setState(
      {
        monday: { startTime: value, endTime: "" }
      },
      () => console.log(this.state)
    );
  };

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

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

  render() {
    return (
      <div className="App">
        <Table
          onSubmit={this.handleFormSubmit}
          onChange={this.handleInput}
          edit={this.state.editMode}
        />
        <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}>save</button>
            </div>
          )}
        </div>
      </div>
    );
  }
}

Table Component:

    export default class Table extends React.Component {
    constructor(props) {
        super(props);

    this.state = {
      weekDays: moment.weekdays(true)
    };
  }

  submitTime = data => {
    this.props.onSubmit(data);
  };

  render() {
    return (
      <table style={{ textAlign: "center" }}>
        <thead>
          <tr>
            <td style={{ opacity: 0 }} />
            {this.state.weekDays.map(day => {
              return (
                <td key={day}>
                  <p>{day}</p>
               </td>
              );
            })}
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>
              <p>Opening:</p>
            </td>
            {[0, 1, 2, 3, 4, 5, 6].map(open => {
              return (
                <td key={open}>
                  {this.props.edit ? (
                    <form onSubmit={this.props.onSubmit}>
                      <input
                        onChange={this.props.onChange}
                        placeholder="open time"
                        defaultValue={open}
                      />
                    </form>
                  ) : (
                    <p>{open}</p>
                  )}
                </td>
              );
             })}
          </tr>
          <tr>
             <td>
              <p>Closing:</p>
            </td>
            {[0, 1, 2, 3, 4, 5, 6].map(close => {
              return (
                <td key={close}>
                  {this.props.edit ? (
                    <form onSubmit={this.props.onSubmit}>
                      <input
                        onChange={this.props.onChange}
                        placeholder="close time"
                        defaultValue={close}
                      />
                    </form>
                  ) : (
                    <p>{close}</p>
                   )}
                </td>
              );
            })}
          </tr>
        </tbody>
      </table>
    );
  }
}

Thank you!

2 Answers 2

3

In the onChange function of each input, you could send the index of the input as a parameter :

{[0, 1, 2, 3, 4, 5, 6].map(close => {
    return (
        <td key={close}>
            {this.props.edit ? (
                <form onSubmit={this.props.onSubmit}>
                    <input
                        onChange={this.props.onChange(close)}

You will now have to add another set of parameters to your handling function :

handleInput = index => e => {

And the last step is to simply map your index to the correct day :

const day = ['monday', 'tuesday', 'wednesday'...][index]

And modify your state depending on the day selected :

this.setState(
    {
        [day]: { startTime: value, endTime: "" }
    }
)

Personally, I would recommend mapping over the array of days directly instead of [0, 1, 2, 3, 4, 5, 6] to remove a step, but that's up to you.


EDIT

Just saw your code, since you have 2 types of input it may lead to other problems. To differentiate the value sent, you can do the exact same thing as before and send the type of value you want to modify :

For the start time :

<input
    onChange={this.props.onChange(open, 'startTime')}
    placeholder="close time"
    defaultValue={close}
/>

For the end time :

<input
    onChange={this.props.onChange(close, 'endTime')}
    placeholder="close time"
    defaultValue={close}
/>

Then change your handling function to get the parameters :

handleInput = (index, type) => e => {

Now the tricky part is the setState. You can not update a single element in a nested object, you will have to deconstruct the old object, and override the corresponding value :

this.setState(prev =>
    ({
        [day]: { 
            ...prev[day],
            [type]: value
        }
    })
)
Sign up to request clarification or add additional context in comments.

7 Comments

+1 this is indeed the way to go with updatnig dynamic key in state. This is called computed property names, refer to docs here: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Thanks, I'm going to check this
Could you apply your solution to my CodeSandBox working example? @Treycos
I think I changed everything you needed in the answer, what are you struggling with ?
you provided a value for startTime, but how you provide for the endTime? Because in the table, startTime and endTime are in separate <tr>. The rest I understood
|
0

Yes it is possible to do this. It works in the same way as updating any object.

let monday = Object.assign({}, this.state.monday)
monday.startTime = value_1
monday.endTime = value_2

this.setState({ monday: monday })

Note the use of Object.assign(). If you don't create a copy of the object, then you will be updating the state directly. For example:

let monday = this.state.monday
monday.startTime = value_1

is the same as doing:

this.state.monday = value_1

Which you should never do in React.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.