admin管理员组

文章数量:1435859

Should ES6 classes be used directly as React state?

I want to define an ES6 class that:

  1. Has member variables that will be displayed on the frontend. (changes to them trigger re-renders)
  2. Has methods that sync those member variables with my backend periodically as they change.

However, calling setState does not appear to diff class members, at least as far as I can tell.

Using the following class:

class Document{
  constructor(){
    this.title = "";
    this.body = "";
  }


  syncWithDatabase = async () => {
    // do some logic to update the database 
  }

}

And this ponent:


// import Document from "...";

export default function Sandbox() {
  const [document, setDocument] = useState(new Document());
  const [renderTrigger, setRenderTrigger] = useState(false);

  return (
    <div>
      <div>{document.title}</div>
      <div>{document.body}</div>
      <button
        onClick={() => {
          document.title = 'Some Default Title';
          document.body = 'lorem text';
          document.syncWithDatabase(); // being able to take this type of action in this way is why I'm trying to use classes. 
          setDocument(document);
        }}
      >
        Set Canned Data
      </button>

      <div>Render trigger is: {renderTrigger ? 'true' : 'false'}</div>
      <button onClick={() => setRenderTrigger(true)}>Force Render</button>

   </div>
  );
}

Clicking the first button will set the title and body on the instance of Document held react state, but it will not update the UI.

Clicking the second button to force a re-render in a way that I am confident will work makes the updated members of document render out, even though they didn't when setDocument is called.

Creating a new object with new Document() and passing it setDocument WILL trigger a re-render. So I'm thinking that react isn't doing a deep pare or is seeing that the reference to the Document object has not changed, and therefore not re-rending.

So, is it possible to change an object's members, pass that object to a setState hook and have it update the UI, without creating an entirely new object? Or should I avoid doing what I'm trying to do here?

Should ES6 classes be used directly as React state?

I want to define an ES6 class that:

  1. Has member variables that will be displayed on the frontend. (changes to them trigger re-renders)
  2. Has methods that sync those member variables with my backend periodically as they change.

However, calling setState does not appear to diff class members, at least as far as I can tell.

Using the following class:

class Document{
  constructor(){
    this.title = "";
    this.body = "";
  }


  syncWithDatabase = async () => {
    // do some logic to update the database 
  }

}

And this ponent:


// import Document from "...";

export default function Sandbox() {
  const [document, setDocument] = useState(new Document());
  const [renderTrigger, setRenderTrigger] = useState(false);

  return (
    <div>
      <div>{document.title}</div>
      <div>{document.body}</div>
      <button
        onClick={() => {
          document.title = 'Some Default Title';
          document.body = 'lorem text';
          document.syncWithDatabase(); // being able to take this type of action in this way is why I'm trying to use classes. 
          setDocument(document);
        }}
      >
        Set Canned Data
      </button>

      <div>Render trigger is: {renderTrigger ? 'true' : 'false'}</div>
      <button onClick={() => setRenderTrigger(true)}>Force Render</button>

   </div>
  );
}

Clicking the first button will set the title and body on the instance of Document held react state, but it will not update the UI.

Clicking the second button to force a re-render in a way that I am confident will work makes the updated members of document render out, even though they didn't when setDocument is called.

Creating a new object with new Document() and passing it setDocument WILL trigger a re-render. So I'm thinking that react isn't doing a deep pare or is seeing that the reference to the Document object has not changed, and therefore not re-rending.

So, is it possible to change an object's members, pass that object to a setState hook and have it update the UI, without creating an entirely new object? Or should I avoid doing what I'm trying to do here?

Share Improve this question asked Oct 27, 2019 at 17:44 Hunter HestonHunter Heston 231 silver badge3 bronze badges 3
  • I'd avoid doing that. Modifying an instance's members doesn't modify the instance itself, so an equality check such as === is going to identify your instances as the same thing each time. – Jamie Dixon Commented Oct 27, 2019 at 17:46
  • Actually your document should be a custom hook. The state should only contain data, and should not be mutated – Jonas Wilms Commented Oct 27, 2019 at 17:50
  • You should not mutate state in React. – HMR Commented Oct 27, 2019 at 19:29
Add a ment  | 

1 Answer 1

Reset to default 5

You can (but probably shouldn't, see below) use an object created by a constructor function (which is what document is in your code) as state. What you can't do is directly modify it as you are here (see the documentation):

document.title = 'Some Default Title'; // <=== INCORRECT
document.body = 'lorem text';          // <=== INCORRECT
document.syncWithDatabase();
setDocument(document);                 // <=== INCORRECT

Instead, you'd need to create a new document object

const newDoc = new Document();
newDoc.title = 'Some Default Title';
newDoc.body = 'lorem text';
newDoc.syncWithDatabase();
setDocument(newDoc);

That said, when using the useState hook, you're usually better off keeping your state variables discrete (having one for title and one for body), so that changing one doesn't require also changing the other (unless, of course, they always change together). The documentation discusses that here; here's one quote:

...we remend to split state into multiple state variables based on which values tend to change together.

(their emphasis)

本文标签: reactjsShould Javascript ES6 Classes be Used as React StateStack Overflow