user3651521
user3651521

Reputation:

The most robust way to setState in React JS

I have a firebase realtime database, a JSON based no sql database, I have 5 fields in that database, storing these:

field 1: STRING
field 2: STRING
field 3: STRING
field 4: STRING
field 5: ARRAY OF OBJECTS

so that database with real example looks something like this:

name: STRING
age: NUMBER
email: STRING
phoneNumber: STRING
skills: OBJECT[]

now I am downloading all these info from the firebase as a single variable(var) called snapshot.val(). and then updating all the states using setState at once like this:

this.setState({
  state_name: snapshot.val().name,
  state_age: snapshot.val().age,
  state_email: snapshot.val().email,
  state_phoneNumber: snapshot.val().phoneNumber,
  state_skills: snapshot.val().skills
});

now this statement works fine if all the values are there in the database and it also works fine if the strings aren't there in the database but it runs into an error if the skills field is undefined in the database.

So setState doesn't throw an error when strings are undefined but it does throw error when arrays are undefined. it happened even with simple string array. Now if one field doesn't exist and an error is thrown, the entire setState fails, so in summary, if multiples states are being updated at once and one of the state upadate fails with an exception or error, all the updates fail and I have been updating such delicate data with separate setState statements with only one state being updated inside its own try catch block. So to update multiple states that have the potential to crash, I am using multiple try catch blocks. something like this:

try {
  this.setState({
    ownerNameValue: snapshot.val().owner_name,
  });
} catch {}

try {
  this.setState({
    phoneNumberValue: snapshot.val().phone_number,
  });
} catch {}

try {
  this.setState({
    specialityValue: snapshot.val().specialty,
  });
} catch {}

try {
  if (snapshot.val().services != undefined) {
    this.setState({
      selectedOptionSerivce: snapshot.val().services,
    });
  }
} catch {}

try {
  if (snapshot.val().home_service != undefined) {
    this.setState({
      selectedOption: snapshot.val().home_service,
    });
  }
} catch {}

Now is there a more robust and efficient way to achieve this?

Some way to update all the states at once and not crash if one of the state has its value as undefined or simply fails. So that way, the states that run into no problems, get updated, but the states that run into exceptions and errors, simply get discarded without showing any error.

so it will be something like this:

field 1: STRING ->> state updated
field 2: STRING ->> state updated
field 3: STRING ->> state updated
field 4: STRING ->> state updated
field 5: undefined ->> state update FAILED (an array of objects was excepted)

NOTICE: this question is not limited to firebase, its about setState handling exceptions and errors of all kind.

Upvotes: 0

Views: 524

Answers (3)

goto
goto

Reputation: 4435

First, you don't need to wrap this.setState inside of a try/catch. setState will not throw an error if you try to set some property of your state to undefined - this is definitely allowed.

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { foo: "bar" };
  }

  componentDidMount() {
    setTimeout(() => {
      this.setState({
        foo: undefined
      });
    }, 4000);
  }

  render() {
    return this.state.foo !== undefined ? (
      <h1>this.state.foo is not undefined</h1>
    ) : (
      <h1>this.state.foo IS undefined</h1>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById("app")
)
<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="app"></div>

So, when you say that it "fails" because you set a piece of state to undefined, you're most likely not handling this case elsewhere in your code. This is common when you're expecting something to be an array and you're trying to use the map method, but it turns out to be undefined.

render() {
  // this.state.skills is `undefined
  // and you're trying to use the `map` method
  // which will fail
  return (
    <div>
      {this.state.skills.map(...)}
    </div>
  )
}

What you should do is to specify fallbacks for items that come back as undefined:

class App extends React.Component {
  updateState = () => {
    firebase()
      .then(snapshot => {
        this.setState({
          state_name: snapshot.val().name ||  '',
          state_age: snapshot.val().age || 0,
          state_email: snapshot.val().email || '',
          state_phoneNumber: snapshot.val().phoneNumber || '',
          state_skills: snapshot.val().skills || []
        })
      })
      .catch(/* handle errors appropriately */)
  }
}

Upvotes: 0

n1stre
n1stre

Reputation: 6086

There is absolutely no sense in doing something like:

  try {
    if (snapshot.val().home_service != undefined) {
      this.setState({
        selectedOption: snapshot.val().home_service
      });
    }
  } catch{ }

Calling setState won't crash if you set undefined as a value. The only case you may face an error is when you accidentally forgot to check this value inside of a lifecycle methods or render:

render() {
  return this.state.skills.map(...)
}

Here, if skills is undefined you'll get an error. For efficiency sake you may do something like:

class Some {
  constructor() {
    this.state = {...}
  }

  get skills() {
    return this.state.skills || []
  }

  render() {
    return this.skills.map(...)
  }
}

Upvotes: 0

SuleymanSah
SuleymanSah

Reputation: 17888

You can assign to skills an empty array when it is undefined like this:

this.setState({
  state_name: snapshot.val().name,
  state_age: snapshot.val().age,
  state_email: snapshot.val().email,
  state_phoneNumber: snapshot.val().phoneNumber,
  state_skills: snapshot.val().skills || [],
});

Upvotes: 1

Related Questions