setState 在数据修改时不触发重新渲染(状态类型为变量)

问题描述 投票:0回答:1

我使用setState更改了状态,但它没有触发重新渲染。我认为这是一个小问题,所以我广泛搜索,但找不到解决方案。

我想在选中第一个复选框时选中第二个和第三个复选框,并取消选中它们。

这是我的代码:

import React, { useState, useEffect } from 'react';
import styled from 'styled-components';

import Button from '@components/common/Button';
import Input from '@components/common/Input';
import CheckBox from '@components/common/CheckBox';

import { LuChevronDown, LuChevronUp } from 'react-icons/lu';

// 임시 데이터
import { tempTerms } from '@constants/tempData';

const BasicRegistration: React.FC = () => {
  const [agreeChecked, setAgreeChecked] = useState(false);
  const [agreeChecked2, setAgreeChecked2] = useState(false);
  const [agreeChecked3, setAgreeChecked3] = useState(false);
  const [viewFullTerms, setViewFullTerms] = useState(false); // 전문 보기

  // 체크박스 핸들러 - 이름 변경 필요
  const handleCheckBoxChange = () => {
    setAgreeChecked((prevState) => !prevState);
  };

  useEffect(() => {
    if (agreeChecked) {
      setAgreeChecked2(true);
      setAgreeChecked3(true);
    } else {
      setAgreeChecked2(false);
      setAgreeChecked3(false);
    }
  }, [agreeChecked]);

  useEffect(
    () => {
      console.log(agreeChecked, agreeChecked2, agreeChecked3);
    },
    [agreeChecked2, agreeChecked3]
  );

  const handleCheckBoxChange2 = () => {
    setAgreeChecked2((prevState) => !prevState);
  };
  const handleCheckBoxChange3 = () => {
    setAgreeChecked3((prevState) => !prevState);
  };

  // 전문 보기
  const handleViewFullTerms = () => {
    setViewFullTerms((prevState) => !prevState);
  };

  return (
    <Container>
      <Section>
        <Heading>아이디</Heading>
        <Input />
      </Section>

      <Section>
        <Heading>회원정보 입력</Heading>
        <P>닉네임*</P>
        <Input />
        <P>핸드폰 번호*</P>
        <Input />
      </Section>

      <Section>
        <Heading>이용약관 동의</Heading>
        <Hr />
        <CheckBox
          onChange={handleCheckBoxChange}
          value="agree"
          checked={agreeChecked}
          text="모두 동의합니다."
          textColor="black"
        />
        <SmallP>만 14세 이상(필수), 이용약관(필수)</SmallP>

        <Hr />
        <CheckBox
          onChange={handleCheckBoxChange2}
          value="agree2"
          checked={agreeChecked2}
          text="(필수) 만 14세 이상입니다."
          textColor="black"
        />
        <CheckBox
          onChange={handleCheckBoxChange3}
          value="agree3"
          checked={agreeChecked3}
          text="(필수) 이용 약관 동의"
          textColor="black"
        />
        <TermCheckContainer>
          {viewFullTerms && <LuChevronUp />}
          {!viewFullTerms && <LuChevronDown />}
          <Button
            buttonStyle="link"
            buttonSize="sm"
            onClick={handleViewFullTerms}
          >
            전문 보기
          </Button>
        </TermCheckContainer>
        {viewFullTerms && (
          <TermContainer>
            <P2>{tempTerms}</P2>
          </TermContainer>
        )}
      </Section>

      <Section>
        <div>
          <Button buttonStyle="square-green" buttonSize="md">
            가입하기
          </Button>
        </div>
      </Section>
    </Container>
  );
};

export default BasicRegistration;

const Container = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  width: 80%;

  & > * {
    width: 100%;
  }
`;

const Section = styled.div`
  display: flex;
  flex-direction: column;
  padding: 10px 0;

  & > * {
    margin: 4px 0;
  }

  input {
    width: 100%;
  }
`;

const Heading = styled.p`
  font-size: var(--font-size-hd-1);
  padding: 4px 0;
`;

const SmallP = styled.p`
  font-size: var(--font-size-sm-1);
  color: var(--color-grey-1);
`;

const P = styled.p``;

const Hr = styled.hr`
  border: 0;
  border-top: 1px solid var(--color-grey-2);
`;

const P2 = styled.p`
  white-space: pre-wrap;
`;

const TermCheckContainer = styled.div`
  padding-top: 8px;
  display: flex;
`;

const TermContainer = styled.div`
  padding: 12px;
  border: solid black 0.5px;
  font-size: var(--font-size-sm-1);
  line-height: 1.5;
  color: var(--color-grey-1);
`;

这是子组件(CheckBox):

import React, { useState, useEffect } from 'react';
import styled, { css } from 'styled-components';

// 체크박스 텍스트 컬러 props
interface StyledCheckBoxProps {
  textColor?: 'grey' | 'black';
}

const textColors = {
  grey: css`
    color: var(--color-grey-1);
  `,
  black: css`
    color: var(--color-black);
  `,
};

interface CheckBoxProps extends StyledCheckBoxProps {
  value: string;
  checked: boolean;
  text: string;
  onChange: (checked: boolean) => void;
}

const CheckBox: React.FC<CheckBoxProps> = ({
  value,
  checked = false,
  text,
  onChange,
  textColor = 'grey'
}) => {
  const [isChecked, setIsChecked] = useState(checked);

  const handleCheck = () => {
    const newChecked = !isChecked;
    setIsChecked(newChecked);
    onChange(newChecked);
  };

  useEffect(() => {
    console.log(isChecked)
  }, [isChecked])
  

  return (
    <Label htmlFor={value} className="chk_box">
      <Input
        type="checkbox"
        id={value}
        checked={isChecked}
        onChange={handleCheck}
      />
      <TextContainer>
        <Span className={isChecked ? 'on' : ''} />
        <Text textColor={textColor}>{text}</Text>
      </TextContainer>
    </Label>
  );
};

export default CheckBox;

const Label = styled.label`
  display: inline-block;
  position: relative;
  cursor: pointer;
`;

const Input = styled.input`
  display: none;
`;

const TextContainer = styled.div`
  display: flex;
  align-items: center;
`;

const Span = styled.span`
  width: 16px;
  height: 16px;
  border: 1px solid var(--color-grey-2);
  position: relative;
  margin-right: 4px;

  &.on {
    background-color: var(--color-green-main);
    display: flex;
    justify-content: center;
    align-items: center;

    &::after {
      content: '';
      width: 6px;
      height: 9px;
      border: solid var(--color-white);
      border-width: 0 2px 2px 0;
      transform: rotate(45deg);
      position: absolute;
      top: 0px;
    }
  }
`;

const Text = styled.p<StyledCheckBoxProps>`
  margin: 0;
  font-size: var(--font-size-ft-1);
  ${(props) => props.textColor && textColors[props.textColor]}
  font-weight: var(--font-weight-regular);
`;

我进行了很多搜索,但找不到任何解决方案。我是一个完全的初学者,所以可能会遗漏一些东西。你能帮我吗?

reactjs rendering setstate
1个回答
0
投票

这是因为 useState 将使用一个值进行初始化,而设置该值的唯一方法是通过 setState

在复选框组件中你正在做这件事

const [isChecked, setIsChecked] = useState(checked);

这样做要小心(从 prop 初始化一个状态),因为你可以想象,如果 prop 改变,useState 会意识到这一点,并且会检查 will change ,但不会那样工作

为什么不直接将 prop 传递给将要使用该 prop 的组件而不是创建一个新状态

复选框组件重构

const CheckBox: React.FC<CheckBoxProps> = ({
  value,
  checked = false,
  text,
  onChange,
  textColor = 'grey'
}) => {
  return (
    <Label htmlFor={value} className="chk_box">
      <Input
        type="checkbox"
        id={value}
        checked={checked}
        // the onChange prop is already doing the thing that you need
        onChange={onChange}
      />
      <TextContainer>
        <Span className={checked ? 'on' : ''} />
        <Text textColor={textColor}>{text}</Text>
      </TextContainer>
    </Label>
  );
};

关于其他文件,尽量不要总是使用 useEffect -> 仅当有必要时,我的想法是:“如何在没有效果的情况下实现此目的 -> 如果没有 -> 使用效果”

大多数人在并非绝对必要的情况下使用了很多 useEffects

const BasicRegistration: React.FC = () => {
  const [agreeChecked, setAgreeChecked] = useState(false);
  const [agreeChecked2, setAgreeChecked2] = useState(false);
  const [agreeChecked3, setAgreeChecked3] = useState(false);
  const [viewFullTerms, setViewFullTerms] = useState(false); // 전문 보기

  // 체크박스 핸들러 - 이름 변경 필요
  const handleCheckBoxChange = () => {
    setAgreeChecked(!agreeChecked);
    setAgreeChecked2(!agreeChecked);
    setAgreeChecked3(!agreeChecked);
  };

  const handleCheckBoxChange2 = () => {
    setAgreeChecked2((prevState) => !prevState);
  };
  const handleCheckBoxChange3 = () => {
    setAgreeChecked3((prevState) => !prevState);
  };

  // 전문 보기
  const handleViewFullTerms = () => {
    setViewFullTerms((prevState) => !prevState);
  };

  return (
    <Container>
      <Section>
        <Heading>아이디</Heading>
        <Input />
      </Section>

      <Section>
        <Heading>회원정보 입력</Heading>
        <P>닉네임*</P>
        <Input />
        <P>핸드폰 번호*</P>
        <Input />
      </Section>

      <Section>
        <Heading>이용약관 동의</Heading>
        <Hr />
        <CheckBox
          onChange={handleCheckBoxChange}
          value="agree"
          checked={agreeChecked}
          text="모두 동의합니다."
          textColor="black"
        />
        <SmallP>만 14세 이상(필수), 이용약관(필수)</SmallP>

        <Hr />
        <CheckBox
          onChange={handleCheckBoxChange2}
          value="agree2"
          checked={agreeChecked2}
          text="(필수) 만 14세 이상입니다."
          textColor="black"
        />
        <CheckBox
          onChange={handleCheckBoxChange3}
          value="agree3"
          checked={agreeChecked3}
          text="(필수) 이용 약관 동의"
          textColor="black"
        />
        <TermCheckContainer>
          {viewFullTerms && <LuChevronUp />}
          {!viewFullTerms && <LuChevronDown />}
          <Button
            buttonStyle="link"
            buttonSize="sm"
            onClick={handleViewFullTerms}
          >
            전문 보기
          </Button>
        </TermCheckContainer>
        {viewFullTerms && (
          <TermContainer>
            <P2>{tempTerms}</P2>
          </TermContainer>
        )}
      </Section>

      <Section>
        <div>
          <Button buttonStyle="square-green" buttonSize="md">
            가입하기
          </Button>
        </div>
      </Section>
    </Container>
  );
};
© www.soinside.com 2019 - 2024. All rights reserved.