1)플러그인 설치하기
2) yarn 에서 styled-components 설치하기
yarn add styled-components
3) 코드 작성
// src/App.js
import React from "react";
import styled from "styled-components";
// 1. styled-components를 만들었습니다.
// styled 키워드를 사용해서 styled-component방식으로 컴포넌트를 만듦
// 내가 원하면 styled.span 이나 styled.h1 이런식으로 쓸 수 있음
const StBox = styled.div`
width: 100px;
height: 100px;
border: 1px solid ${(props) => props.borderColor}; // 4.부모 컴포넌트에서 보낸 props를 받아 사용합니다.
margin: 20px;
`;
const boxList = ["red", "green", "blue"];
// 색을 넣으면, 이름을 반환해주는 함수를 만듭니다.
const getBoxName = (color) => {
switch (color) {
case "red":
return "빨간 박스";
case "green":
return "초록 박스";
case "blue":
return "파란 박스";
default:
return "검정 박스";
}
};
// 조건부 styling :
const App = () => {
return (
<div>
{/* 2. 그리고 위에서 만든 styled-components를 사용했습니다. */}
{/* 3. 그리고 props를 통해 borderColor라는 값을 전달했습니다. */}
{/* 이런식으로 조건부 styling 가능 */}
{/* <StBox borderColor="red">빨간 박스</StBox>
<StBox borderColor="green">초록 박스</StBox>
<StBox borderColor="blue">파랑 박스</StBox>
<boxList borderColor=""></boxList> */}
{boxList.map((box)=>{
<StBox borderColor={box}>{getBoxName(box)}</StBox>
})}
</div>
);
};
export default App;
>> 리액트 훅
*useState
useState는 가장 기본적인 hook이며, 함수 컴포넌트에서 가변적인 상태를 가지게 해준다.
const [state, setState] = useState(initialState);
원래는 useState 라는 함수가 배열을 반환을 하고, 이것을 구조 분해 문법으로 꺼내놓은 모습일 뿐이다.
만약 state가 원시 데이터타입이 아닌 객체 데이터 타입인 경우에는 불변성을 유지해줘야 한다.
> 리액트 상태가 불변해야 하는 이유?
리액트의 상태가 불변하다는 것 → 기존 상태(state) 값을 직접 바꾸지 않는다.
상태 값을 변경해야 한다면 ‘깊은 복사’ 후 변경하여 할당해야 한다.
리액트는 state가 변경될 때 리렌더링 된다.
리액트 입장에서 상태의 변경 기준은 '참조 값의 변경'이다.
아무리 값을 변경하더라도 참조 값이 같다면, 리액트는 state의 변경을 인식할 수 없고 리렌더링 되지 않는다.
클래스형 컴포넌트의 경우 render() 메서드를 통해 상태 변경을 감지할 수 있다.
상태 변경이 감지되면 필요한 부분만 업데이트할 수 있다.
함수형 컴포넌트는 말 그대로 컴포넌트를 리턴하는 '함수'다. 따라서 렌더링된다는 건 함수를 다시 호출한다는 말과 같다. (렌더링 = 함수 호출)
따라서 함수형 컴포넌트의 경우 상태 관리를 위해서 함수의 상태를 기억하는 어떤 장치가 필요하다. 이 때 사용하는 메서드가 바로 useState Hook이다.
useState는 자바스크립트 특징 중 하나인 '클로저'를 통해 함수의 기본 state 값을 기억한다. 기본 state와 변경된 state를 비교해주는 함수가 바로 useState이다.
useState 함수를 직접 만들어보자.
// 우리가 평소에 사용하는 useState 형태 const [state, setState] = useState(initialValue); // state 값을 변경할 때 setState(변경값); // useState 함수를 직접 만들어보자. const useState = (initialValue) => { // 처음: 기본 값을 파라미터로 받아서 변수에 저장 // state변경시: setState 함수에 의해 새로운 값이 기본 값에 담긴다. let 기본값 = initialValue; // 기본값을 리턴하는 함수 생성 // 기본값이 setState 함수에 의해 변경되었다면 변경 값 리턴 const state = () => 기본값; // 내부 scope const setState = (변경값) => { // 클로저를 통해 기본값에 접근 가능 // 변경값이 파라미터를 통해 들어오면 기본값에 넣는다. 기본값 = 변경값; } return [state, setState]; } |
setState를 사용하는 방식에는 우리가 알고 있는 방식이 아닌 또 다른 방식이 있다.
함수형 업데이트 방식 인데
// 기존에 우리가 사용하던 방식
setState(number + 1);
// 함수형 업데이트
// 괄호 안에 () : state를 가져올 수 있고, {} 안에는 값을 변경하는 코드 작성
setState(() => {});
위 코드와 같이 setState의 ( ) 안에 수정할 값이 아니라, 함수를 넣는 것이다.
그리고 그 함수의 인자에서는 현재의 state을 가져올 수 있고, { } 안에서는 이 값을 변경하는 코드를 작성할 수 있다.
// 현재 number의 값을 가져와서 그 값에 +1을 더하여 반환한 것 입니다.
setState((currentNumber)=>{ return currentNumber + 1 });
일반 업데이트 방식은
onClick 안에 있는 함수가 각각 실행되는 것이 아니라 ‘batch’ 로 처리가 된다.
우리가 onClick을 했을때, setNumber를 3번 명령 내리지만 리액트에서는 하나로 모아서 최종적으로 한번만 실행시킨다는 것
함수형 업데이트 방식은
이 3번을 동시에 명령을 보이면, 순차적으로 각각 1번씩 실행을 시킨다.
>> useEffect
useEffect는 리액트 컴포넌트가 렌더링(어떤 화면이 그려지는 것)될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook
쉽게 말해 어떤 컴포넌트가 화면에 보여졌을 때 내가 무언가를 실행하고 싶다면? 또는 어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하고 싶다면? useEffect를 사용한다.
useState와 마찬가지로 React에서 제공하는 훅 (기능) 이므로, import React, { useEffect } from "react"; 로 import 해서 사용한다.
브라우저에서 우리가 App 컴포넌트를 눈으로 보는 순간, 즉 App 컴포넌트가 화면에 렌더링될 때 useEffect 안에 있는 console.log가 실행된다.
컴포넌트가 렌더링 될 때 실행된다. 이게 바로 useEffect 핵심 기능이다.
import React, { useEffect } from "react"; const App = () => { useEffect(() => { // 이 부분이 실행된다. console.log("hello useEffect"); }); return <div>Home</div>; } export default App; |
useEffect는 useEffect가 속한 컴포넌트가 화면에 렌더링 될 때 실행된다.
이런 useEffect의 특징에 의해 우리가 의도치않은 동작을 경험할수도 있다.
만약, input이 있고 value 라는 state를 생성하여 input과 연결을 시키고 브라우저에 input에 어떤 값을 입력하면 useEffect가 계속 실행되는 것을 볼 수 있다.
import React, { useEffect, useState } from "react"; const App = () => { const [value, setValue] = useState(""); useEffect(() => { console.log("hello useEffect"); }); return ( <div> <input type="text" value={value} onChange={(event) => { setValue(event.target.value); }} /> </div> ); } export default App; |
>> 의존성 배열
useEffect에는 의존성 배열이라는 것이 있다.
“이 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행할게” 라는 함수이다.
// useEffect의 두번째 인자가 의존성 배열이 들어가는 곳 useEffect(()=>{ // 실행하고 싶은 함수 }, [의존성배열]) |
useEffect에 의존성 배열을 추가했음.
의존성 배열안에는 어떠한 값도 넣지 않았다.. 의존성 배열이 “이 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행할게” 라는 의미를 가지므로, 배열안에 아무것도 넣지 않았으니 useEffect는 처음에 딱 한번만 실행되고 그 이후로는 어떤일이 일어나도 실행이 되지 않는다.
반대로 의존성 배열안에 value 값을 넣게 되면?
빈 배열을 넣었을 때, 최초 렌더링 이후에는 useEffect가 실행되지 않았는데
의존성 배열에 value 를 넣어보게 되면,, value는 state이므로 input을 입력할 때마다 그 값이 변하게 되니 useEffect도 계속 실행이 될 것
>>클린업
클린 업 이란?
useEffect는 “어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하고 싶다면”이라고 사용했는데, 컴포넌트가 나타났을 때 (렌더링됐을 때 === 함수 컴포넌트가 실행됐을 때) useEffect의 effect 함수가 실행되는 것을 알았고
반대로 컴포넌트가 사라졌을 때 무언가를 어떻게 실행하는 코드→ 클린 업 (clean up) 이라고 표현
클린업 방법 : useEffect 안에서 return 을 해주고 이 부분에 실행되길 원하는 함수를 넣으면 된다.
// src/App.js import React, { useEffect } from "react"; const App = () => { useEffect(()=>{ // 화면에 컴포넌트가 나타났을(mount) 때 실행하고자 하는 함수넣는 곳 return ()=>{ // 화면에서 컴포넌트가 사라졌을(unmount) 때 실행하고자 하는 함수넣는 곳 } }, []) return <div>hello react!</div> }; export default App; |
컴포넌트가 화면에 mount, unmount 되었다가 정확한 표현
리액트의 Lifecycle에 대해 검색해보기
→ 진심 중요하다는 내용 다 검색하게 할거면 뭐하러 강의하는거지….?
실제로, 화면을 벗어났을때 클린업 함수가 실행되도록 코드 짜보기
// src/SokSae.js import React, { useEffect } from "react"; import { useNavigate } from "react-router-dom"; const 속세 = () => { const nav = useNavigate(); useEffect(() => { return () => { console.log( "안녕히 계세요 여러분~!" ); }; }, []); return ( <button onClick={() => { nav("/todos"); }} > 속세를 벗어나는 버튼 </button> ); }; export default 속세; |
>> 리덕스
리덕스를 사용하면State를 공유하고자 할때 부-모 관계가 아니여도 되고, 중간에 의미없이 컴포넌트를 거치지 않아도 된다. 그리고 자식 컴포넌트에서 만든 State를 부모 컴포넌트에서도 사용할 수 있게 된다.
- Global state와 Local state
Local state (지역상태) 란?
- 컴포넌트에서 useState를 이용해서 생성한 state
- 좁은 범위 안에서 생성된 State 라고 생각
Global state (전역상태)란?
- Global state는 컴포넌트에서 생성되지 않는다. 중앙화 된 특별한 곳에서 State들이 생성된다.
- 중앙 State관리소에서 State를 생성하고, 만약 어떤 컴포넌트에서 State가 필요하다면 컴포넌트가 어디에 위치하고 있든 상관없이 State를 불러와서 사용 할 수 있는 것
이렇게 특정 컴포넌트에 종속되어 있는 것이 아니라 “중앙 state 관리소”에서 생성된 State를 Global state라고 한다. 그리고 이러한 값들을 관리하는 것을 전역 상태 관리 라고 한다.
리덕스란,
우리가 위에서 말한 “중앙 state 관리소”를 사용할 수 있게 도와주는 패키지(라이브러리)이다.
프론트엔드 개발자들은 “리덕스”를 전역 상태관리 라이브러리 라고 많이 표현한다.
전역 상태, 즉 Global State를 의미하고 그것을 관리하게 도와주는 라이브러리 (패키지) 이기 때문
reducer는 함수다
store는 상태를 저장하는 장소이다.
dispatch = 어떤 action을 일으킨다.
reducer는 우리가 action을 일으켰을때 자동실행된다.
→ store에 있는 데이터를 바꿔주는 역할을 reducer가 한다.
아직 저 상태는 module과 store가 연결이 되지 않은 상태이다.
이렇게 configStore.js에 와서 저 밑줄친 2개 코드를 작성해주면, module과 store가 연결된 상태이다.
이렇게 스토어와 모듈을 연결시키는 작업은, 우리가 모듈을 1개씩 추가할 때마다 똑같이 진행해주면 된다.
** 모듈? Module은 프로그램을 구성하는 내부의 코드가 기능별로 나뉘어져있는 형태를 의미함
1) 한 파일에 쓰여진 코드를 여러 파일로 관리할 수 있으며
2) 다른 모듈로부터 특정 기능을 가져오거나, 다른 모듈이 특정 기능을 참조할 수 있게 함
>> 우리가 모듈과 스토어을 잘 연결했는지 확인하는 방법?
component에서 store를 직접 조회해보면 된다. -> useSelector라는 훅을 사용
리덕스 정리
- 모듈 생성 ( state와 리듀서 생성)
- 모듈을 store에 연결
- 컴포넌트에서 store에 있는걸 불러올때 useSelector를 사용한다.
>> 리듀서는 변화를 일으키는 함수다.
리듀서에게 변경하는 명령을 내리는 것 = 액션 (리듀서에게 내가 어떤 액션을 하길 원한다)
리덕스에서는 그것을 엑션이라고 한다.
원하는 명령을 ‘객체'로 만드는 것 → action객체는 반드시 type이라는 key를 가져야 한다.
우리가 action객체를 보내면, 리듀서는 객체 안에서 type이라는 키를 확인하기 때문
액션객체를 보내려면 ‘useDispatch’라는 훅을 사용해야 한다.
– > dispatch란 action객체를 reducer로 보내는 전달객체이다.!!
action객체의 type의 value는 대문자로 작성한다.
>> action Creator
위 코드처럼 직접 하드코딩을 하는 것이 아니라, 액션객체를 한 곳에서 관리할 수 있도록 “함수"와 액션 value를 상수로 만들어주는 것
만약 PLUS_ONE 이라는 액션 객체를 만드는 함수를 만든다면 아래와 같이 만들 수 있다.
그리고 우리는 이것을 Action Creator라고 부른다.. 해석 그대로 액션을 만드는 생성자 인 것
// src/redux/modules/counter.js const PLUS_ONE = "PLUS_ONE"; // value는 상수로 생성 // 액션객체를 반환하는 함수 생성 // export 가 붙는 이유는 plusOne()는 밖으로 나가서 사용될 예정이기 때문입니다. export const plusOne = () => { return { type: PLUS_ONE, // type에는 위에서 만든 상수로 사용 (vscode에서 자동완성 지원) }; }; |
>> payload
증가 시킬 숫자를 카운터 프로그램을 사용하는 사용자가 직접 정할 수 있게 기능을 만들기
사용자가 5을 더하고 싶으면 어떤 input에 5를 입력해서 버튼을 누르면 5가 더해지고, 10을 더하고 싶으면 10을 입력하고 버튼을 눌렀을 때 10이 더해지는 프로그램
“N을 더해” 라고 N을 같이 리듀서에서 보내는 것
. 지금까지는 ‘~을’ 이라는 목적어가 없었다면, 이제는 그 목적어가 생긴것이고 목적어도 액션객체에 담아 같이 보내줘야 할 것이다.
이렇게 액션객체에 같이 담아 보내주는 것을 payload라고 한다.
만약 10을 더해 라는 것을 리듀서에게 보내고 싶으면 액션객체에 payload를 같이 담아주는 것.
// payload가 추가된 액션객체
{type: "ADD_NUMBER", payload: 10}
⭐️ 꼭 payload라는 이름을 통해서 보내야하나?
리덕스는 굉장히 유연한 라이브러리이기 때문에 많은 것들이 “표준화”되어 있지 않다. 때문에 자신만의 방식으로 프로그래밍 할 수 있는 유연성을 제공한다. 리덕스 공식 문서를 확인해보면 액션은 객체이며 해당 액션이 어떤 기능을 수행해야 하는지 명시하는 type이라는 프로퍼티를 반드시 가져야 한다고 나와있다. 그래서 지금까지는 아래와 같이 액션 객체에 type 프로퍼티를 추가해 어떤 기능을 수행해야 할지 명시해줬었지만 그 외에 데이터들을 어떤 프로퍼티에 값으로 넣어줘야 하는지는 개발자 마음
{type: "ADD_NUMBER"}
{type: "ADD_NUMBER", num: 10}// ?? {type: "ADD_NUMBER", number: 10} // ?? {type: "ADD_NUMBER", data: 10} // ?? {type: "ADD_NUMBER", myNumber: 10} // ?? {type: "ADD_NUMBER", myNum: 10} // ?? {type: "ADD_NUMBER", payload: 10} |
위 코드들은 모두 유효한 코드이지만, 데이터를 ‘payload’ 프로퍼티에 담아주는 이유는 커뮤니티에서 이렇게 하면 제일 좋더라 하는 “커뮤니티 best practice”로 공유되면서 많은 개발자가 데이터는 payload라는 프로퍼티에 담아주고 있기 때문.
.
jsx에서 a태그를 사용하려면 반드시 link태그를 이용해야 하는데
a 태그를 사용하면 브라우저가 새로고침이 되어버리기 때문(모든 컴포넌트가 다시 렌더링됨)
useNavigate 아니면 link 사용
>>children
어떤 컴포넌트들은 어떤 자식 엘리먼트가 들어올지 알수 없을수도 있음
children, props
compositon ? 합성이라고??
'🌼 리액트 공부' 카테고리의 다른 글
리액트 네이티브 첫 수업 (실시간 강의) 정리 #1 (0) | 2022.12.29 |
---|---|
[리액트] Carousel (이미지 자동 슬라이드) 기능 구현하기 (0) | 2022.12.26 |
리액트 심화 강의 복습 / redux-toolkit , json-server, thunk , Axios (1) | 2022.12.23 |
[리액트] 리액트 입문 강의 정리노트 (0) | 2022.12.05 |
[리액트] 리액트 입문강의 듣는중 / 리액트의 'state'란? (0) | 2022.12.04 |
댓글