我使用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);
`;
我进行了很多搜索,但找不到任何解决方案。我是一个完全的初学者,所以可能会遗漏一些东西。你能帮我吗?
这是因为 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>
);
};