close
Skip to content

State updated before call to shouldComponentUpdate in shallow render #1970

@hisuwh

Description

@hisuwh

Current behavior

Given a simple component like this:

import * as React from "react";

interface Data {
    value: string;
}

export class SimpleComponent extends React.Component<Data, Data> {

    protected static getDerivedStateFromProps(props: Data, state: Data) {
        if (props.value === state.value) {
            return null;
        }

        console.log("updating state: ", props.value);
        return {
            value: props.value
        };
    }

    public state: Data = {
        value: this.props.value
    };

    public shouldComponentUpdate(nextProps: Data, nextState: Data) {
        console.log("state: ", this.state);
        console.log("nextState: ", nextState);
        console.log("props: ", this.props);
        console.log("nextProps: ", nextProps);
        const shouldUpdate = nextState.value !== this.state.value;
        console.log("shouldUpdate: ", shouldUpdate);
        return shouldUpdate;
    }

    public render() {
        console.log("render: ", this.state.value);
        return (
            <input value={this.props.value} />
        );
    }
}

I would expect the following test to pass:

import * as React from "react";
import { Expect, Test, TestFixture, FocusTest } from "alsatian";
import { shallow } from "enzyme";

import { SimpleComponent } from "./SimpleComponent";

@TestFixture("SimpleComponent")
export class SimpleComponentTests {

    @FocusTest
    @Test("should update value from props")
    public shouldUpdateValueFromProps() {

        const wrapper = shallow(<SimpleComponent value="initial" />);
        Expect(wrapper.find("input").prop("value")).toEqual("initial");
        wrapper.setProps({ value: "updated" });
        Expect(wrapper.find("input").prop("value")).toEqual("updated");
    }
}

However it fails as shouldComponentUpdate returns false. Looking at the logs we see the following:

# FIXTURE SimpleComponent
render:  initial
updating state:  updated
state:  { value: 'updated' }
nextState:  { value: 'updated' }
props:  { value: 'initial' }
nextProps:  { value: 'updated' }
shouldUpdate:  false
not ok 1 should update value from props
 ---
   message: "Expected \"initial\" to be equal to \"updated\"."
   severity: fail
   data:
     got: "initial"
     expect: "updated"
 ...

As you can see from the above this.state appears to have already been updated by the time shouldComponentUpdate gets called resulting in it returning false and the test failing as it never re-renders.

If I run the same component in the browser I get the following output:

render:  initial
updating state:  updated
state:  {value: "initial"}
nextState:  {value: "updated"}
props:  {value: "initial"}
nextProps:  {value: "updated"}
shouldUpdate:  true
render:  updated

Which is what I would expect.

Expected behavior

The state of the component should not be updated before calling shouldComponentUpdate.

Your environment

not sure whats needed here

API

  • shallow
  • mount
  • render

Version

library version
enzyme ^3.8.0
react ^16.3.0
react-dom ^16.5.0
react-test-renderer ~16.5.2
adapter (below) ^1.7.1

Adapter

  • enzyme-adapter-react-16
  • enzyme-adapter-react-16.3
  • enzyme-adapter-react-16.2
  • enzyme-adapter-react-16.1
  • enzyme-adapter-react-15
  • enzyme-adapter-react-15.4
  • enzyme-adapter-react-14
  • enzyme-adapter-react-13
  • enzyme-adapter-react-helper
  • others ( )

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions