Toskan
Toskan

Reputation: 14961

React useState broken? state is not updated correctly

see here:

https://codesandbox.io/s/nervous-hermann-5wsph

basically:

they keyboard used in the example, seems to get a wrong copy of the state

it fixes once you press shift on the keyboard. But other than that? the keyboard onKeyPress has the wrong useState variables.

what is the problem?

This: enter image description here

the debug text is supposed to change IN THE CONSOLE, but doesn't. That means whatever I fire onKeyPress will have the wrong copy of the state variables except the ones I call in the callback.

That is just very very bad. I thought useState will solve those problems, not make them harder ?! Am I missing something?

Upvotes: 1

Views: 307

Answers (1)

JMadelaine
JMadelaine

Reputation: 2964

The issue is not with React state but with the Keyboard component.

react-simple-keyboard does not realize that props have changed, so the onKeyPress function that you pass it always has the initial value.

When React state changes (the debug text), your onKeyPressed function in the MyComponent component will be recreated with the new values. In turn, the onKeyPress function inside your KeyboardWrapper also gets recreated.

This new function gets passed as a prop to Keyboard, but in the source code for Keyboard (see here) a check is performed to see whether the props have changed.

The function that performs this check can be found here:

export const propsChanged = (prevProps, props) => {
  const cleanProps = sourceObj =>
    JSON.stringify({
      ...sourceObj,
      stateToIgnore: null
    });

  return cleanProps(props) !== cleanProps(prevProps);
};

This function has a bug. When the props are 'cleaned', all functions are removed - which is a bit silly, since a function is a valid prop.

So the previous props (which includes your old onKeyPress function with the initial value of debugText) is 'cleaned' and compared with the new 'cleaned' props, and it looks to the Keyboard that the props have not changed, since neither the previous clean props or new clean props, have the onKeyPress function.

I added some logging to that function:

const previousProps = {
  onKeyPress: () => {
    console.log("Initial text");
  },
};

const newProps = {
  onKeyPress: () => {
    console.log("New text");
  },
};

const propsChanged = (prevProps, props) => {
  const cleanProps = (sourceObj) =>
    JSON.stringify({
      ...sourceObj,
      stateToIgnore: null,
    });

  console.log("original prev props:", prevProps);
  console.log("clean prev props:", cleanProps(prevProps));
  console.log("original new props:", props);
  console.log("clean new props:", cleanProps(props));

  return cleanProps(props) !== cleanProps(prevProps);
};

console.log(
  "does keyboard think props have changed?",
  propsChanged(previousProps, newProps)
);

console.log("have props actually changed?", previousProps !== newProps);

console.log("is there a bug with react-simple-keyboard?", true);

and here is the result:

original prev props: { onKeyPress: [Function: onKeyPress] }
clean prev props: {"stateToIgnore":null}
original new props: { onKeyPress: [Function: onKeyPress] }
clean new props: {"stateToIgnore":null}
does keyboard think props have changed? false
have props actually changed? true
is there a bug with react-simple-keyboard? true

I suggest filing an issue over on their github repo.

Upvotes: 3

Related Questions