5

I'm using a lightweight ORM to connect my react app with an external service... This package returns objects of your model and allows you to perform operations directly against them. While this is really awesome, I'm struggle to figure out how I can include these objects in state and still follow the "never modify state directly" tenant of react.

If I had a component that updated the account name, would it be acceptable to do something like this?

interface IAppState {
  account: Account
}

class App extends React.Component<{}, IAppState> {
  constructor(props) {
    super(props);
    this.state = {
      account: new Account()
    }
  }

  //set the new name and update the external service
  public updateAccount = (newName: string)=>{
    account.name = newName; //REDFLAG!!!
    acc.update().then(()=>{
        this.setState({ account: this.state.account })
    })
  }

  //retrieve our account object from external service
  public componentDidMount() {
    const qParams = queryString.parse(window.location.search);
    Account.get(qParams.externalId).then((acc)=>{
        this.setState({account: acc})
    })
  }

  render() {
    return <NameEditor handleClick={this.updateAccount} account={a} />
    }
 }

I guess I could avoid mutating state by initiating a blank ORM object, copying the properties over, sending the update and then setting state, but this seems like a major pain.. Especially since these ORM objects can contain child ORM objects that I'd like to be able to modify as well.

Is the way I'm mutating state above "dangerous" or "bad form"???

Update

Did some reading and it definitely seems like this is probably bad form and might be navigated elegantly using the react/addons... However, what if the ORM call has a side effect on the object? For example, calling insert sets the objects external id field.

NSjonas
  • 10,693
  • 9
  • 66
  • 92

2 Answers2

4
public updateAccount = (newName: string)=>{
    //account.name = newName; //REDFLAG!!!
    // You can use the below code to update name after it is updated
    // on server.
    // This will work because the object being passed here
    // will be merged with Component state.
    acc.update().then(()=>{
        this.setState({account: {name : newName}})
    })
}

Directly modifying state is not recommended as react will not come to know of the change and it will not cause a rerender.

All the diffing happens on Virtual DOM and react only updates the change attributes to Browser DOM. You can read more about the react diffing algorithm here.

Surender
  • 757
  • 5
  • 12
  • worth noting that this example would NOT update the remote system with the new name. I think what I'll have to do, is create a deep-clone of the object, update it and then pass it in. Seems inefficient – NSjonas Sep 16 '17 at 16:28
  • @NSjonas it depends on what the api needs, it could be as simple as acc.update({name: newName}).then( /* rest of the code to update state */) if api allows you to update just the name. – Surender Sep 18 '17 at 15:56
1

React recommends to use immutable objects for setting state. you can either use Object.assign or immutable.js for this purpose which will make our life easier. If you mutate your state object it will affect the the performance of your react component.

You can refer the below link for more information.

https://facebook.github.io/react/docs/optimizing-performance.html#examples

Roopak Puthenveettil
  • 1,387
  • 2
  • 13
  • 27