2
\$\begingroup\$

Starting from React 16.3 there are some new lifecycle methods introduced. One of them is the static getDerivedStateFromProps(props,state) method. This method essentially synchronizes properties and state.

The props and state of a react component aren't always a one-on-one match. But for simple components often a couple of props do match exactly on the state.

It's not strictly necessary to have this getDerivedStateFromProps(...) method actually. If your component doesn't really have an internal state, then there is no need to worry about it.

Anyway, in a case where there is an exact match of props and state I am trying to find the most elegant way to write this code.

The following is rather verbose:

static getDerivedStateFromProps(props, state) {
  // these 3 properties can change over time.
  const activeBaseNode = props.activeBaseNode;
  const filterOID = props.filterOID;
  const quickFilterRequest = props.quickFilterRequest;

  // compare them to the state
  if ((state.filterOID !== filterOID) || 
    (state.activeBaseNode !== activeBaseNode) || 
    (state.quickFilterRequest !== quickFilterRequest)) {

    // updates the state
    return ({ activeBaseNode, filterOID, quickFilterRequest });
  }

  // no changes --> no updates
  return null;
}
| improve this question | | | | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Why do you need to clone them into state? You would be able to use this.props directly inside your component? \$\endgroup\$ – TryingToImprove Oct 29 '18 at 15:42
  • \$\begingroup\$ this.props changes will not cause a rerender \$\endgroup\$ – bvdb Oct 29 '18 at 15:48
  • \$\begingroup\$ That depends on your shouldComponentUpdate. React wants to limit the usage of getDerivedStateFromProps - see reactjs.org/blog/2018/06/07/… \$\endgroup\$ – TryingToImprove Oct 29 '18 at 16:10
  • \$\begingroup\$ I would have to guess the users that voted to put this on hold as off topic didn't understand what they were looking at. @TobySpeight \$\endgroup\$ – Ted Oct 30 '18 at 14:29
  • 1
    \$\begingroup\$ @Ted, that's spot on ! My guess is that @TobySpeight, @Quill, @t3chb0t and @Dannnno are not aware of the changes in react version 16.3 which introduced the getDerivedStateFromProps method. I don't see how any more context than that is possibly needed. reactjs.org/blog/2018/03/29/react-v-16-3.html \$\endgroup\$ – bvdb Oct 31 '18 at 13:25
3
\$\begingroup\$

Ted's answer is quite readable to me.

You could use some to detect if you need to submit changes as that is semantically closer to what you are doing:

if (['activeBaseNode', 'filterOID', 'quickFilterRequest']
    .some(prop => state[prop] !== props[prop])

It's perhaps nicer to reverse the logic with every and early exit, then extract all the properties only when you need them. Here I'm doing it with a function, avoiding defining three consts:

static getDerivedStateFromProps(props, state) {
  if (['activeBaseNode', 'filterOID', 'quickFilterRequest']
    .every(prop => state[prop] === props[prop]) return null

  return (({activeBaseNode, filterOID, quickFilterRequest}) => 
         ( {activeBaseNode, filterOID, quickFilterRequest}))(props)

That's cryptic and short approach.

I don't really like that the names of the properties are repeated three times in all the solutions. To avoid that, a reduce can be used to build the properties that need to be updated:

const toUpdate = ['activeBaseNode', 'filterOID', 'quickFilterRequest']
  .find(prop => state[prop] !== props[prop])
  .reduce((m,k) => m[k] = props[k], {})
return Object.keys(toUpdate).length == 0 ? null : toUpdate

Once you get used to it, I think that's the clearest. It's got the essence of the problem and nothing more: the names of the properties, that the state != the props, and that you return null if nothing is changing.

| improve this answer | | | | |
\$\endgroup\$
  • \$\begingroup\$ After reading the answer of Ted, I was also going in the direction that you put here. Not bad. I just wish there was a way to do it without declaring the fields as strings, ('cause I just know, that's where I'll make bugs/typos). But hey, It's an improvement for sure. \$\endgroup\$ – bvdb Oct 30 '18 at 9:18
2
\$\begingroup\$

Here is one possibility:

static getDerivedStateFromProps(props, state) {
  const { activeBaseNode, filterOID, quickFilterRequest } = props;

  let updateState = false;
  ['activeBaseNode', 'filterOID', 'quickFilterRequest'].forEach(item => {
    if(state[item] !== props[item]){
      updateState = true;
    }
  });

  return updateState ? ({ activeBaseNode, filterOID, quickFilterRequest }) : null;
}
| improve this answer | | | | |
\$\endgroup\$
  • \$\begingroup\$ I really like the improvement of the const {a,f,q} = props which is straightforward. I'm not a big fan of string arrays. But hey, it's an improvement for sure. \$\endgroup\$ – bvdb Oct 30 '18 at 9:14

Not the answer you're looking for? Browse other questions tagged or ask your own question.