简体   繁体   中英

React component re-renders when updating state

I'm working on a page with multiple steps, based on the current step (tracked by useState ) it renders a different component.

I have a parent component ( App.js ) which tracks all data changes, and each step is in its own component. In the Step component, I notice that whenever I select an option, a re-render occurs and the selection is lost. (Observed by the console.log at the top of the component in Step1.js )

How do I ensure that a re-render does not occur so that it properly tracks the selection?

App.js

import React, { useState } from "react";
import { Button, Space } from "antd";

import Step1 from "./Step1";

import "antd/dist/antd.css";

const Step2 = () => {
  console.log("Rendering Step 2");
  return (
    <div>
      <b>Step 2</b>
    </div>
  );
};

const Step3 = () => {
  console.log("Rendering Step 3");
  return (
    <div>
      <b>Step 3</b>
    </div>
  );
};

export default () => {
  const [step, setStep] = useState(0);
  const [items, setItems] = useState({});

  const next = () => {
    setStep(step + 1);
  };

  const prev = () => {
    setStep(step - 1);
  };

  React.useEffect(() => {
    console.log("Items:", items);
  }, [items]);

  const updateItem = (key, value) => {
    setItems({
      [key]: value
    });
  };

  const stepProps = {
    updateItem: updateItem
  };

  const StepContent = props => {
    switch (step) {
      case 0:
        return <Step1 {...props} />;
      case 1:
        return <Step2 {...props} />;
      case 2:
        return <Step3 {...props} />;
      default:
        return <b>Error</b>;
    }
  };

  const StepBar = () => {
    return (
      <div>
        <Space>
          {step > 0 && (
            <Button
              size="large"
              onClick={() => prev()}
              style={{ minWidth: "100px" }}
            >
              Previous
            </Button>
          )}
          {step < 2 && (
            <Button
              size="large"
              onClick={() => next()}
              style={{ minWidth: "100px" }}
              block
            >
              Next
            </Button>
          )}
        </Space>
      </div>
    );
  };

  return (
    <div className="App">
      <StepContent {...stepProps} />
      <StepBar />
    </div>
  );
};

Step1.js

import React from "react";
import { Radio } from "antd";

const Step1 = ({ updateItem }) => {
  console.log("Rendering Step 1");

  const onChange = event => {
    const value = event.target.value;
    updateItem("option", value);
  };

  return (
    <div>
      <Radio.Group onChange={onChange}>
        <Radio value={"a"}>A</Radio>
        <Radio value={"b"}>B</Radio>
      </Radio.Group>
    </div>
  );
};

export default Step1;

Codesandbox example: https://codesandbox.io/s/stoic-cartwright-sr8ur

It seems like you did not pass the actual value in your Radio.Group thus you're not getting the actual value of it.

Add items in your stepProps

const stepProps = {
    updateItem,
    items
  };

In your Page1.js add items in your destructured props and add a value prop to Radio.Group like this:

const Step1 = ({ items, updateItem }) => {
  console.log("Rendering Step 1");

  const onChange = event => {
    const value = event.target.value;
    updateItem("option", value);
  };

  return (
    <div>
      <Radio.Group value={items["option"] || null} onChange={onChange}>
        <Radio value={"a"}>A</Radio>
        <Radio value={"b"}>B</Radio>
      </Radio.Group>
    </div>
  );
};

It would be better if you also separate other Steps in a separate file to avoid confusions.

Here is mysandbox

I checked the values by logging them in

StepContent= ...

The values are being saved regardless of the step i checked by going forward and backward and as the log was showing me the state every time this method fired up i am sure the values are there in the state but on thing i noticed is that you are not showing the values selected in radio group as selected you need to pass the state to props to the radio group as well to show the selected button as checked

A re-render can only be triggered if a component's state has changed.By default, shouldComponentUpdate returns true .

function shouldComponentUpdate(nextProps, nextState) {
        return true;
    }

That's what causes the “update everything all the time”.When React comes to render the component it will run shouldComponentUpdate and see if it returns true.When you use shouldComponentUpdate you'll need to decide which bits of data actually matter for the re-render.

shouldComponentUpdate(nextProps) {
        return nextProps.id !== this.props.id;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM