Component

lee Samse·2024년 7월 2일

react-course

목록 보기
3/8
post-thumbnail

리액트는 레고처럼 블럭들을 조립하는 방식으로 구성된다. 레고블럭이 콤포넌트에 해당하며 함수형 또는 클래스형으로 정의할 수 있으며 style-component 라이브러리로 css free한 컴포넌트 작성방법도 제공한다.

// 함수형 정의
function App() {
	return (
		<div className="App">
			<p> Hello World!</p>
		</div>
	);
}
const App = () => {
	return (
		<div className="App">
			<p> Hello World!</p>
		</div>
	);
}
// typescript로 함수형 정의
const App: React.FC<MyProps> = ({title}) => {
	// ...
}

// 클래스형 정의
class App extends Component {
	render() {
		return <div className="App">
			<p> Hello World!</p>
		</div>
	}
}

모든 콤포넌트의 라이프사이클에 해당하는 mount, update, unmount 이벤트가 있다.
mount시에는 다음의 순서로 호출된다.

constructor() -> render() -> componentDidMount()

update시에는 다음의 순서로 호출다.

render() -> componentDidUpdate()

unmount시에는 componentDidUnmount()가 호출된다.
필요에 따라 해당 이벤트를 오버라이드하여 사용 가능하다.

Props

콤포넌트에 속성을 지정하고자 할때 속성을 전달하는 방식을 정의한다.

먼저 interface 키워드로 Props명세를 정의한다.

interface ContainerProps{
  bgColor: string;
}

function component에서는 props: ContainerProps 또는 { 인자들 }: ContainerProps 형태로 사용한다.

const Circle = ({ bgColor }: ContainerProps) => {
  return <Container bgColor={bgColor}/>;
}

const Circle = (props: ContainerProps) => {
  return <Container bgColor={props.bgColor}/>;
}

필수인자 뿐 아니라 옵셔널 인자도 설정이 가능하다.

interface CircleProps{
  bgColor: string;
  borderColor?: string;
  text?: string;
};

변수명 뒤에 ?를 기입하면 되고 그러면 태그로 사용 시 값을 지정하지 않아도 되며 지정하지 않았을 때는 디폴트값이 적용되어야 하는데 이는 ?? 키워드를 이용하여 설정이 가능하다.

const Circle = (props: CircleProps) => {
  return <Container bgColor={props.bgColor} borderColor={props.borderColor ?? "blue"}>{props.text ?? "default text"}</Container>;
}

함수인자를 아래와 같이 구성하면 옵셔널을 더 쉽게 정의할 수 있다.

const Circle = ({ bgColor, borderColor, text="default text"}: CircleProps) => {
  return <Container bgColor={bgColor} borderColor={borderColor ?? bgColor}>{text}</Container>;
}

styled-component에서는 <ContainerProps> 형식으로 전달하고 props.{속성이름}으로 접근하여 사용한다.

const Container = styled.div<ContainerProps>`
  background-color: ${(props) => props.bgColor};
`;

const Container = styled.div<{bgColor: string}>`
  background-color: ${(props) => props.bgColor};
`;

사용자 이벤트

콤포넌트를 작성할때 이벤트를 새로 정의하고자 한다면 먼저 interface를 정의한다.

닫기 아이콘을 콤포넌트로 만든다고 할때 닫기 이벤트를 먼저 정의한다.

interface CloseBtnProps {
  onClose: () => void;
}

콤포넌트를 정의할때 타입을 정의한 interface로 한다.

const CloseIcon: React.FC<CloseBtnProps> = ({ onClose }) => {
  return <Close onClick={() => { onClose(); }}>
      <IoCloseCircleOutline
          style={{
              width: "20px",
              height: "20px",
              color: "white",
          }} />
  </Close>;
}

const CloseIcon: React.FC<CloseBtnProps> 부분이 중요한데 타입을 이렇게 지정하지 않으면 이벤트를 전달할 수 없다.
이렇게 정의된 콤포넌트는 다음과 같이 사용 가능하다.

<CloseIcon onClose={() => {
	// 닫기 이벤트 처리 부분.
}} />

State

state는 콤포넌트에서 사용할 수 있는 값이 유지되는 변수로 이해하면 된다.
const나 let으로 선언한 일반 변수와 다른점은 이 값이 변하면 해당 변수를 사용하는 콤포넌트들 재랜더링된다. 따라서 콤포넌트에서 변경이 되어야 하는 변수들을 다룬다.

useState를 임포트

import { useState } from 'react';

useState 선언

const [state, setState] = useState(초기값(생략 가능));
  • state는 현재값이며 setState는 해당값을 변경하는 함수이다. state값을 변경하고자 하면 setState(xx)와 같이 호출해야 한다.
  • let으로 선언하지 않는 이유는 state = xx와 같이 변수를 재할당하지 않도록 하기 위해서이다.
  • setState는 비동기 함수이다. 즉 setState를 호출한다고 값이 바로 바뀌지 않는다.
  • 만일 setState가 함수안에서 여러번 호출된다면 비동기 처리가 되면서 누적된 setState를 한번에 처리되는데 마지막 호출로 덮여저서 마지막 호출만 처리될 수 있다.
const plus = () => {
  setCount(count + 1);
  setCount(count + 2);
  setCount(count + 3);
}
** 카운트가 6이 증가해야 할것 같지만 setCount가 누적이 되지 않고 마지막 호출만 처리되면서 3만 증가하게 된다. 주의해야 할 사항이다.
  • 만일 6씩 증가하게 하고 싶다면 setCount함수에 현재값을 인자로 넘겨서 하면된다.
const plus = () => {
	setCount(count => count + 1);
	setCount(count => count + 2);
	setCount(count => count + 3);
}
** 안전하게 현재값을 넘겨받는 형식을 주로 사용하길 바란다.

useEffect

어떤 값의 상태가 변할 때 마다 특정 함수가 호출되도록 해주는 훅이다.

useEffect(함수, [변수목록])

const [todos, setTodos] = useState([]);
useEffect(() => {
    localStorage.setItem("todos", JSON.stringify(todos));
}, [todos]);

todos의 내용이 변경될 때마다 localStorage에 값을 저장하는 예이다.
useEffect의 두번째 인자에 빈 배열을 추가하면 콤포넌트가 처음 렌더링될때만 함수가 호출된다. 이후 렌더링이 다시 발생할때는 무시된다. 함수형 콤포넌트의 생성자 역할을 하게 된다.

const MyComp = () => {
	useEffect(() => {
		console.log("한번만 호출됨");
	}, []);
	console.log("렌더링될때마다 호출됨");
	return <></>;
}

Styled Component

콤포넌트의 css styling을 편하게 하기 위해 고안되었다.
Theme나 Props로 스타일을 간편하게 관리가 가능하다. 그리고 콤포넌트별로 스타일을 관리할 수 있다.

프로젝트에 추가하려면 다음 명령을 실행한다.

npm install --save styled-components

다음 형식으로 기존 태그를 새로운 태그로 정의할 수 있다.

const NewComponent = styled.[태그명]

다음은 div를 새로 Father라는 콤포넌트로 정의한 예이다.

const Father = styled.div`
	display: flex;
`

function App() {
 return <Father></Father>
}

Props로 프로퍼티를 넘겨서 해당 콤포넌트의 스타일을 변경할 수 있다.

const Box = styled.div<{bgColor: string}> `
	background-color: ${(props) => props.bgColor};
	width: 100px;
	height: 100px;
`
function App() {
  return <Father>
	  <Box bgColor="tomato" />
	  <Box bgColor="wheat" />;
  </Father>
}

상속을 통해 부모 콤포넌트의 속성을 내려 받을 수 있다.

const Circle = styled.Box `
  border-radius: 50px;
`

function App() {
  return <Father>
    <Box bgColor="tomato" />
    <Circle bgColor="orange"/>
}

as 키워드를 tag에 사용하여 실제 렌더링 될 때 태그를 변경할 수 있다. 이는 기존 styled component의 css속성들을 그대로 유지하면서 태그만 변경하여 그 태그의 기능을 사용하고 싶을 때 유용하다.
아래는 Box형태를 유지한 채 해당 박스가 a 태그로 동작하게 하는 예이다.

function App() {
  return <Father>
    <Box bgColor="tomato" />
    <Box as="a" href="/" bgColor="orange"/>
}

위코드는 두개의 Box 태그를 사용하였지만 실제 렌더링 될 때 두번째 Box는 스타일을 유지하면서 anchor 태그로 렌더링 되는것을 알 수 있다.

attrs를 styled component 정의시 추가하여 디폴트 값을 지정할 수 있다.

const Box = styled.div.attrs({
	background-color: tomato;
})`
  width: 100px;
  height: 100px;
`;

styled component가 하위요소들을 가지고 있을 때 상위 콤포넌트에서 하위요소를 select하여 속성을 변경할 수 있다.

const MainContainer = styled.section`
    display:flex;
    width:100vw;
`

const Memo = styled.div`
    width:300px;
    height:500px;
`

const ShadowMemo = styled(Memo)`
    box-shadow: 2px 4px 8px;
funtion Section2 () {
  return(
    <>
    <MainContainer>
      <Memo>Lorem Ipsum</Memo>
      <ShadowMemo>Lorem Ipsum</ShadowMemo>
    </MainContainer>
      <Memo>Lorem Ipsum</Memo>
    </>
  )
}

MainContainer안에 Memo와 Memo를 상속한 ShadowMemo가 있다. 여기서 Memo의 background-color만 변경하고자 할때 Memo에서 변경한다면 ShadowMemo도 변경되어버리기 때문에 원하는 동작이 아니게 된다. 이런 경우 MainContainer에서 selector를 통하여 접근하여 변경이 가능하다.

const MainContainer = styled.section`
    display:flex;
    width:100vw;
    ${Memo} {
      background-color:black;
      color: white;
    }
`

${콤포넌트명}으로 하위 콤포넌트에 접근하여 다른 속성을 줄 수 있다.

다음은 여러가지 select예제이다.
BtnArea 아래 a 태그들 중에 첫번째만 flex: none;을 적용하고 나머지는 다른 속성을 적용하도록 할 수 있으며 a 태그 내의 span의 스타일을 따로 적용하는 예제이다.

export const BtnArea = styled.div`
    display: flex;
    flex-wrap: wrap;
    margin-top: 1.6rem;
    gap: 0.8rem;

    a {
        width: 100%;
        height: 4.8rem;
        margin: 0;
        border-radius: 0.8rem;
        color: #fff;
        border: 0.1rem solid #815DFB;
        background-color: #815DFB;
        display: inherit;
        justify-content: center;
        align-items: center;

        &:first-child {
            flex: none;
        }
        &:not(first-child) {
            height: 4rem;
            flex: 1;
        }
        span {
            width: 100%;
            display: inherit;
            justify-content: center;
            font: inherit;
        }
    }
`;

이렇게 div로 감싸진 내부의 태그들을 선택적으로 다른 속성을 부여하고자 할때 다양한 방법으로 적용해줄 수 있다.

profile
삼스입니다.

0개의 댓글