0

Background

I am building an office add-in using their React-based starter kit and TypeScript. I mention this because I am unable to get great debug support as far as I can tell, so I'm unable to see the error message that React is providing in my current situation.

What I'm attempting

(simplifying below. I can be more specific if you'd like; let me know.)

I have an interface for my AppState, and a complex object with some properties:

export interface AppState {
  eventInput: EventInput;
}

export class BookendEventInput {
  public minutesAdjacent: number = 30;
  public subject: string = "";
  public enabled: boolean = false;
  public eventId: string = "";
}

I have one working scenario, which is a checkbox:

<Checkbox id="enableBookendBefore" checked={this.state.eventInput.enabled} onChange={this.eventInputCheckboxChanged}></Checkbox>

That is updating the state via the change function:

eventInputCheckboxChanged = () => {
    this.setState((state: AppState) => {
      var newValue = !this.state.eventInput.enabled;
      var input = state.eventInput;

      input.enabled = newValue;

      state.eventInput = input;
    })
  }

But this isn't working for another scenario.

The Problem

I am now attempting to do something similar with a textbox. I have an input:

<input type="text" id="subject" disabled={!this.state.eventInput.enabled} value={this.state.eventInput.subject} onChange={this.subjectChanged} />

And the change function:

  subjectChanged = (e) => {
    var newSubject = e.target.value;

    this.setState((state: AppState)=> {
      var input = state.eventInput;
      input.subject = newSubject;

      state.eventInput = input;
    })

Expected Behavior: I would expect to see the subject text box & state updated, as if they were two-way bound.

Actual Behavior: The entire screen goes blank/white, indicating that I'm getting a React-level error I believe (since I can't do F12 and can't see debug output due to it being in a task pane inside Outlook.)

The Question

How can I correctly bind a textbox using React, that's tied to a property in an object within state? Is it possible to do this, or am I violating a React principle?

SeanKilleen
  • 8,809
  • 17
  • 80
  • 133
  • It looks like in both cases you're mutating `state` directly, you're supposed to return a new object though. –  Mar 19 '20 at 15:52
  • I would look at the setState call. Looks like you are getting previous state. And then mutating that. You are not returning anything new. – moficodes Mar 19 '20 at 15:54
  • I was under the impression that in that format, passing state within the function means that you pull the complex object, set the property, and then set the root level object again within state, and that's how you update complex objects. I'm going off of this answer: https://stackoverflow.com/a/27105353/316847 – SeanKilleen Mar 19 '20 at 16:07
  • I'm pretty sure that answer is wrong, and so is the one posted below. The line `input.subject = ...` directly modifies `state`, since `input` points to the object referenced by `state.eventInput`. –  Mar 21 '20 at 10:23
  • Does this answer your question? [How to update nested state properties in React](https://stackoverflow.com/questions/43040721/how-to-update-nested-state-properties-in-react) –  Mar 21 '20 at 10:44

1 Answers1

2

In this case, you're using the callback to setState to try and modify state. This is either not firing or causing an infinite loop, I'm unsure of which!

Either way, to correctly modify state you'll want:

subjectChanged = (e) => {
    var newSubject = e.target.value;
    var input = state.eventInput;

    input.subject = newSubject;

    this.setState({eventInput: input});
});

This will achieve what you're looking for.

Ryan Killeen
  • 120
  • 6
  • 1
    This did it! Thank you! Love being related to a developer. :) – SeanKilleen Mar 19 '20 at 16:20
  • Like I commented above, `input.subject = newSubject;` mutates `state`, which is not supposed to happen. This question is a duplicate anyway though. –  Mar 21 '20 at 10:45
  • Hi @ChrisG, strings in JS are immutable. The above example isn't best practices all-around, but I was trying to illustrate what was actually wrong with the code: that they were using the callback from setting state, and weren't actually setting state anywhere. In this case, if you were dealing with objects, you'd absolutely want to make a copy. Because it's a string, it is already immutable. – Ryan Killeen Mar 22 '20 at 17:13