Kimyeongkyung
react 기초 문법 정리 본문
※함수 표현식 & 화살표 함수
1) 함수 표현식
let do_something = function [함수 이름]( ){ ... }
2) 화살표 함수(함수 표현식의 단축형)
let do_something = ( ) => { ... }
class Cat{
constructor(어디선가 받아오는 값 A) {
생성자 함수
this.name= A;
나 자신을 가리키는 키워드
// 어디선가 받아오는 값을 Cat이라는 함수에 넣어주겠다
}
let cat = new Cat("B");//Cat이라는 클래스를 사용해서 새로운 객체를 만드는 키워드//B는 name
cat.showName( );
출력 => B
상속
class MyCat entends Cat{
//A라는 class를 만들건데 A는 Cat이라는 class를 확장해 올거야
constructor(name,age){
//name은 이미 위에 부모 class에 값이 있으니까 this~ 써줄필요 없이 super라는 함수를 써서 가져오자. 일단 부모 class에 접근!
super(name); this.age = age; } showName(){ console.log(this.name + "입니다"); }}let my_cat = new MyCat("B", 4); //name, age
my_cat.showAge();
출력 : 4
my_cat.showName();
출력 : B입니다
//class 이름이 같을 경우 자식클래스에 있는 친구를 우선적으로 가져다 쓰기 때문
※묵시적 형변환
1(숫자) + "2"(문자)출력 : "12"(문자)
※ = / = = /= = =
1) =
:어떤 변수에 값을 할당할 때 사용
2) = =
:등차. 유형(숫자, 문자열..)을 비교하지 않음. 변수 값을 기반으로 비교
ex) 0 = = "0" 은 true
3) = = =
: 유형까지 비교하는 등차.
ex) 1 = = "1" 은 false
※ spread 문법
: 딕셔너리(key,value)나 배열([ ])에 어떤 요소가 들어가 있는데 그 요소들을 전부 객체 바깥으로 끄집어내줌
ex) let a1=[1,2,3]
let a2=[4,5,6]
let a3=[...a1, ...a2]
=> a3 = [1,2,3,4,5,6]
※ 삼항 연산자
: 조건 ? true 출력 or true면 실행할 행동 : false 출력 or false면 실행할 행동
let is= 5;
let e = is === 5 ? 3 : 4;
※ JSX 사용법
※ Array 내장 함수
1)map
: 어디까지 반복할 건지 끝 값을 정해줘야 하는 for문과 달리 map은 알아서 끝까지 돌려줌
const array_num = [0,1,2,3,4,5];
const new_array = array_num.map((array_item)=> {
return array_item + 1; //요소요소들에게 +1을 해준다
})
console.log(new_array);
출력 : [1,2,3,4,5,6]
//원본 배열
console.log(array_num);
출력 : [0,1,2,3,4,5]
//원본 배열에 영향을 주지 않고 새로운 배열을 만들어냄
2)filter
: 어떤 조건을 만족하는 항목들만 골라서 새로운 배열을 만들어주는 것
const array_num = [0,1,2,3,4,5]
const new_array = array_num.filter((array_item) => {
return array_item > 3; //배열에서 3보다 큰 값만 골라내줘
})
console.log(new_array);
//map은 요소에 가공을 할 수 있었다면 filter는 요소 자체를 가공할 수는 없지만
요소들 중 어떤 조건을 만족하는 애들만 찾아낼 때 사용
3)concat
: 두 array를 합쳐주고 싶을 때 사용. 중복 제거는 해주지 않는다!
const array_num = [0,1,2,3,4,5]
const new_array = [4,5]
const merge = array_num.concat(new_array) //두 배열을 합쳐줘
console.log(merge);
출력:[0,1,2,3,4,5,4,5]
4)from
map, filter와 같은 내장함수를 사용할 수 없는 유사배열을 배열로 만들어줄 때, 배열에 들어갈 요소가 확실하지 않지만 길이가 a인 배열을 만들고 싶을때 사용
const my_name="sparta";
const my_name_array = Array.from(my_name);
console.log(my_name_array);
출력: ["s","p","a","r","t","a"];
//문자열을 구성하고 있는 하나하나가 배열의 한 요소가 됨.
...
근데 만약 저 배열을 숫자로 출력하고 싶다면?
...
const my_name="sparta";
const num_array = Array.from(my_name, (item,index) => {return index});
//(item, index)
//(값, 콜백함수(배열 안 각각의 항목))
console.log(num_array)
출력:[0,1,2,3,4,5]
//---------------------------------------------------------------------
//길이가 a인 배열
const new_array = Array.from({length:4},(item, index) => {
return index;
};
console.log(new_array);
출력:[0,1,2,3]
※ 내장 함수 응용해보기
const animals = ["강아지", "고양이", "햄스터", "강아지", "고양이", "고양이", "토끼"];
animals.map((animal) => {
if(animal === "고양이"){ //고양이가 있으면 count 1 증가시켜줘
count += 1;
}
});
console.log(count); //총 count는?
출력 : 3
-----------------------------------------------------------------------------------------
const animals = ["복슬 강아지", "검정 고양이", "노란 햄스터", "강아지", "노랑 고양이", "고양이", "흰 토끼"];
let cats = animals.filter((animal)=>{
return animal.indexOf("고양이") !== -1;
//-1을 쓴 이유? "고양이가 배열안에 들어있지 않다" = 위치가 없다.
// !==을 사용해 고양이라는 단어가 포함된 원소만 찾기
});
console.log(cats);
출력 : ['검정 고양이', '노랑 고양이', '고양이']
문자열의 내장함수 목록 참고링크
https://codedragon.tistory.com/9884
문자열 내장함수, 문자열 내장함수들
문자열 내장함수 · 문자열 자료형이 자체적으로 가지고 있는 함수입니다. · 문자열 내장함수를 사용하기 위해서는 문자열 변수 이름 뒤에 점(.)를 붙인 다음에 사용하고자 하
codedragon.tistory.com
※ JSX 사용법
: HTML을 품은 자바스크립트
- dictionary : (key,value)로 이루어진 자바스크립트 자료형 중 하나
=> JSX 안에서 자바립트 변수나 어떤것을 쓰려면 중괄호 필수
- Style을 줄때도 마찬가지로 그냥 string으로 dictionary(객체) 로 넘겨주어야 하기 때문에 이중 중괄호 필요
=> style = {{ 스타일 속성= "", 스타일 속성 = ""...}}
※ 라이프 사이클 알아보기=> 컴포넌트가 웹페이지에 들어갔다가 사라지기까지의 한 과정.
클래스형 컴포넌트에서만 사용.
함수형 컴포넌트에서는 라이프 사이클을 사용할 수 없기 때문에(constructor에 값을 넣어 초기화를 해줄수가 없다는 뜻!)
리액트 훅을 사용하고,
1) DOM : HTML 단위(요소) 하나하나를 객체 취급하는 모델. 트리구조!
2) 가상 DOM:눈에 보이지 않는 메모리상에서 돌아가는 가짜 DOM.
어떻게 돌아가?
어떤 행동을 하면(변화) 일단 가상 DOM에 올려(새로 그리기) → 진짜 DOM과 비교 → 바뀐부분만 마지막에 진짜 DOM에 반영
진짜 DOM과 가상 DOM을 비교할 때 뭐가 바뀐지 어떻게 알지?
=>그래서 비교를 하기 위해서는 key값이 필요하다!
생성 → 업데이트 → 제거
constructor(생성자 함수) : 컴포넌트에 필요한 어떤 값을 넣어주는 것
render : 변경된 내용이 가상 DOM 에서 진짜 DOM으로 올라가는 과정.
- New props : 부모가 자식에서 주는 데이터가 바뀌었을 때
- setState() : 내가 가진 데이터가 바뀌었을 때
- forceUpdate : 강제로 업데이트 일으킴(강제 수정)
- 부모 컴포넌트가 업데이트 되었을 때
나 이제 바뀔꺼야! 근데 뭘로 바뀔거야?
=>render 안 return 안에 넣어주기
(Mount는 끝난 상태임)
componentDidMount : render가 끝났을때(진짜 DOM에 잘 붙었을 때)
: 첫번째 렌더링이 끝난 후 진짜 DOM에 올라간 뒤 딱 한번만 실행됨. 업데이트(리렌더링) 시에는 실행되지 않음
- 돔 관련 처리 가능
componentDidUpdate : 나 변경사항 업데이트 됐어! (리렌더링)
: 리렌더링이 끝난 후 호출됨( 이미 가상 DOM이 실제 DOM으로 올라간 뒤)
- componentDidUpdate(prevProps, prevState)
→ 업데이트 되기 전의 prop(부모가 준 거)와 state(내 꺼)
- 돔 관련 처리 가능
componentWillUnmount : 나 이제 사라진다!
2)그럼 진짜 DOM은 언제 새로 그리지?
- 처음 페이지 진입 시
- 데이터가 변해서 마지막에 가상돔에서 변경된 부분이 적용될 때
왜 진짜 DOM에 바로 변경사항을 적용 안해? 그렇게 느려?=> 사이트 구조에 따라서 가상 DOM을 쓰는 것보다 진짜 DOM을 직접 만지는게 더 빠를수도! 아니면 더 느릴수도!
props : 부모 컴포넌트. state가 props로부터 데이터를 받아올 경우 변경이 불가능함
state : 생성, 수정이 해당 컴포넌트 내에서만 이루어짐. 그래서 생성도 수정도 자유로움
※다른 파일에 있는 컴포넌트 불러오기
<Bucketlist.js>
import React from "react";
const BucketList = (props) => {
}
//파라미터로 들어온 값은 dictonary 형태임.
//받아온 key값이 list인 무언가가 있는데 이 list에 담긴 어떤 값은 받아온 것이다!
//props, props.list 로 써도 무방함
const BucketList = ( {list} ) => {
const my_lists = list;
}
export default Bucketlist;
(= export {BucketList}; 로 쓸수도 있음)
클래스형 <App.js>
import React from "react";
import Bucketlist from "./BucketList";
class App extends React.Component{
constructor(props){
super(props);
this.state = {
list=["a", "b", "c"],
};
}
render(){
return (
<div className = "App">
<BucketList list={this.state.list}/>
</div>
)
}
}
export default App;
//return되는 값은 무조건 1개 이상 필요하다(null을 넣더라도 꼭!)
함수형<App.js>
import Bucketlist from "./BucketList";
(= import {Bucketlist} from "./BucketList"로 쓸수도 있음)
function App(){
return(
<div className = "App">
<BucketList />
</div>
);
}
export default App;
styled-components
: 컴포넌트에 직접적으로 스타일을 입히는 방식
1) 패키지 설치
yarn add styled-components
2) 사용해보기
<App.js>
import React from "react";
import Bucketlist from "./BucketList";
class App extends React.Component{
constructor(props){
super(props);
this.state = {
list=["a", "b", "c"],
};
}
render(){
//return되는 값은 무조건 1개이상 필요하다(null을 넣더라도 꼭!)
return (
<div className = "App">
<MyStyled bg_color={"red"}/>
<BucketList list={this.state.list}/>
</div>
)
}
const MyStyled = styled.div`
원하는 스타일 작성 후 render 안에 <MyStyled/> 추가해주기
background-color : ${(props) => (props.bg_color)};
//축약 전 코드 : ${(props) => { return props.bg_color }};
//prop로 받아온 것중에 bg_color로 받아온거 꺼내줘
`
export default App;
※ SCSS에서 자주쓰는 문법
1. nesting
: sccs에서는 반복되는 써야하는 걸 한 묶음으로 묶을 수 있음. <MyStyled> 태그 안에 속해있는 <p>태그에 스타일을 주고 싶을때( 각각 styled 객체를 만들 필요가 없음)
<BucketList.js>
<div className = "App">
<MyStyled>
<p></p>
<MyStyled/>
<BucketList list={this.state.list}/>
</div>
const MyStyled = styled.div`
p{
원하는 p태그의 스타일 작성
}
`
2. &은 자기 자신 지칭!
const MyStyled = styled.div`
&:hover( 등의 마우스이벤트 ){
원하는 스타일 작성
}
createRef
: 클래스형 함수에서 리액트 요소를 가져올 수 있는 방법 중 하나(=이름표). 라이프사이클 메서드임.
import React from "react";
import Bucketlist from "./BucketList";
class App extends React.Component{
constructor(props){
super(props);
this.state = {
list=[''a", "b", "c"],
};
this.text = React.createRef( ); //나 ref 만들어서 text로 가져올거야
}
componentDidMount(){
console.log(this.text);
} //마운트 완료
render(){
//return되는 값은 무조건 1개이상 필요하다(null을 넣더라도 꼭!)
return (
<div>
<input type ="text" ref={this.text} onChange = {()=> {console.log(this.text.current.value);}}/> //로 확인가능
</div>
)
}
useRef
:함수형 컴포넌트에서는 라이프 사이클메서드를 사용할 수 없으므로(=constructor 안에서 무언가를 초기화 할 수 없다)
createRef 대신 리액트 훅
// 변수를 만든다.
const my_wrap = React.useRef(초기값);
return (
<div ref={my_wrap}>
{my_lists.map((list,index)=> {
return <ItemStyle key={index}>{list}</ItemStyle>;
});
)
state 관리
import React from "react";
import Bucketlist from "./BucketList";
class App extends React.Component{
constructor(props){
super(props);
this.state = {count:3,};
}}
//count 숫자를 가지고 배열을 만들고, 그 배열만큼 map을 돌려줘야함
component Didmount(){
}
render(){
return (
<div className = "App">
<div style = {{
width:'150px',
height:'150px',
backgroundColor:'#ddd',
}}>
nemo
)}
(1)새 프로젝트 만들기
yarn create react-app nemo
(2) App.js를 class형 컴포넌트로 바꾸고 시작!
// App component를 class형으로!
import React from 'react'; class App extends React.Component { constructor(props){ super(props);
this.state = {} } componentDidMount(){ }
render(){ return ( <div className="App"> </div> ); } }
export default App;
(3) state에 count라는 변수를 추가하고, count 숫자만큼 네모칸을 화면에 띄우기
(4) 더하기, 빼기 버튼을 만들고,
return (
<div className="App"> {nemo_count.map((num, idx) => {
return (
<div key={idx}
style={{ width: "150px", height: "150px", backgroundColor: "#ddd", margin: "10px", }} > nemo</div> ); })}
<div>
<button>하나 추가</button>
<button>하나 빼기</button>
</div>
</div> );
(5) 함수를 만들어서
addNemo = () => { // this.setState로 count를 하나 더해줍니다!
this.setState({ count: this.state.count + 1 }); }; removeNemo = () => {
// 네모 갯수가 0보다 작을 순 없겠죠! if문으로 조건을 걸어줍시다.
if (this.state.count > 0) { // this.setState로 count를 하나 빼줍니다!
this.setState({ count: this.state.count - 1 }); }else{ window.alert('네모가 없어요!'); } };
(6) 연결하자!
return (
<div className="App"> {nemo_count.map((num, idx) => {
return ( <div key={idx} style={{
width: "150px", height: "150px", backgroundColor: "#ddd", margin: "10px", }} >
nemo </div> ); })} <div> {/* 함수를 호출합니다. 이 클래스 안의 addNemo 함수를 불러오기 때문에 this.addNemo로 표기해요. */} <button onClick={this.addNemo}>하나 추가</button> <button onClick={this.removeNemo}>하나 빼기</button> </div> </div> );
※ Ref
우리가 코드에서 다루는 요소는 리액트 요소!
예를들어 input에 무언가을 쓰고 카드를 만들어서 붙인다?
자바스크립트로는 가능하지만 리액트에서는 약간 찝찝한 느낌
input이 없던 페이지에 input을 만들거야!
일단 가상DOM에 input을 추가하고, 이 input을 찾기 위해
getElementById~~를 한다? NO!!!!!!!! 진짜 DOM에는 현재 input이 없기 때문에 오류가 나게됨
그럼 리액트에서는 변경사항이 적용된 가상 DOM(예를 들어 input의 text)을 어떻게 가져오지?
import React from "react";
import styled from "styled-components";
// 함수형 컴포넌트는 이렇게 쓸 수도 있고
//const BucketSecond = (props) => {
// console.log(props);
// return (
// <div>버킷 리스트</div>
// );
// }
//{list}:어떤값은 받아온 것
const BucketSecond = ({ list }) => {
const my_lists = list;
const my_wrap = React.useRef(null);
window.setTimeout(()=>{
console.log(my_wrap);
},1000); //1초 있다가 첫번째 인자에 넣은 함수를 실행하도록 해
return (
<div ref={my_wrap}>
{my_lists.map((list, index) => {
// console.log(list);
return (
<ItemStyle className="list-item" key={index}>
{list}
</ItemStyle>
);
})
}
</div>
);
};
※ eventlistener
: <div onClick = {}>처럼 직접 element에 이벤트를 넣어줄 수 있지만, addEventListener를 통해 이벤트를 넣을 수도 있다!
state와 props 간의 데이터 교환이 잘 이루어짐.
근데 만약 컴포넌트가 많아진다면?
형제관계(sibling) 끼리는 데이터 교환이 불가능..저~아래 자식컴포넌트한테 데이터를 전달해줘야 할때..
방법은 리덕스!
리덕스
※ 라우팅
MPA : HTML이 여러개
SPA : HTML이 딱 하나
페이지를 이동할 때마다 서버는 클라이언트에게 HTML을 주는데
왜 우리는 SPA를 사용하지?
모든 페이지가 다 완전히 다른 뷰를 가지고 있나?
NO!!!
header와 footer는 페이지마다 같다.
만약 MPA를 사용하면 페이지가 이동될때마다 진짜 DOM → 진짜 DOM으로 갈아치우는 과정에서
필요하지 않은 작업을 많이 하게됨(안바뀌는 부분까지 매번 바뀌어버림)
또한 매번 새로고침될때마다 페이지가 바뀌기 때문에 상태값 유지가 안됨
(예를 들어 회원가입 시 쓰고있던 정보가 날라가는 상황 초래)
그럼 SPA는 단점이 없어?
유저가 홈페이지를 방문할때 회원가입을 할지 안할지 모름
회원가입 페이지를 들어갈지 안들어갈지 모른다는뜻
하지만 SPA는 처음부터 모든페이지를 서버로부터 다 받아오기 때문에
첫 로딩시간이 굉장히 느릴 수 있다.
하지만 페이지 간 이동은 이미 모든페이지를 받아온 상태이기 때문에 매우빠름!
한 HTML 안에서 A, B페이지에 필요한 컴포넌트를 각각 묶고, 보여주는 과정을 직접 구현하긴 어려우니까
그걸 도와주는 게 라우팅 라이브러리!
yarn add react-router-dom 설치
URL 파라미터( & 쿼리)
사용자가 어떤 페이지에서 글을 작성했다. 이 글이 뭔지 알아채서 매번 라우터에 추가해줘야 할까?
NO!
URL 파라미터를 통해 똑같은 뷰에 안에있는 데이터만 갈아끼우는 형식으로 사용!
<app.js>
1. useParams 사용하기
/cat/: 변수명(어떤 변수명으로 받아올건지)
import {Route, Link} from "react-router-dom";
import Home from "./Home";
import Dog from "./Dog";
import Cat from "./Cat";
//pate ="/" => /포함하면 메인까지 다보여줘라. exact 추가시 /와 /cat은 다르기 때문에 페이지 이동시 메인화면은 안보이게됨
// "cat/:cat_name" 파라미터로 받아오는 방법
// <Cat />
//props로 받아오는 방법
//<Cat /> 대신 component={Cat} 추가
function App() {
return (
<div className="App">
<div>
<Link to="/">Home으로 이동</Link>
<Link to="/cat">Cat으로 이동</Link>
<Link to="/dog">Dog으로 이동</Link>
</div>
<Route path ="/" exact> {/*완전히 똑같으면 보여줘 */}
<Home />
</Route>
<Route path = "/cat/:cat_name" component={Cat}>
{/* <Cat /> */}
</Route>
<Route path ="/dog">
<Dog />
</Route>
</div>
);
}
export default App;
<Cat.js>
import React from "react";
import { useParams } from "react-router-dom";
const Cat = (props) => {
const cat_name = useParams();
//다른방법
//자식 컴포넌트를 바로 넘겨주는 방법
//App.js에서 Rount 경로 적는 라인에 component={Cat} 추가
console.log(cat_name);
return (<div>고양이 화면 입니다!</div>);
};
export default Cat;
그런데 매일 주소창에 /cat/이동할 페이지의 변수명 을 적어서 이동을 할 수는 없다!
※ <Link /> 사용해서 페이지 이동 링크 만들기
function App() {
return (
<div className="App">
<div>
<Link to="/">Home으로 이동</Link>
<Link to="/cat">Cat으로 이동</Link>
<Link to="/dog">Dog으로 이동</Link>
</div>
.
.
)
}
하지만 링크를 모든곳에 달아줄수는 없다. 이럴땐 어떡해?
※ history 사용해서 페이지 링크 만들기
import React from "react";
import {useHistory} from "react-router-dom";
const Dog = (props) => {
const history = useHistory();
console.log(props);
return (<div onClick={()=>{
history.push("/cat");
}}>멍멍이 화면 입니다!</div>);
};
export default Dog;
※ 페이지 주소가 잘못됐을 때
: 주소가 잘못된 경우 흰 화면만 뜨면 사용자 입장에서 사이트가 이상하다고 생각할 수 있기 때문에 흰 화면만 나오지 않도록 조치가 필요함
1. 잘못된 주소로 접근한 경우 주소가 잘못되었다는 텍스트를 통해 알린다.
2. useHistory를 router-dom에서 가져와서, 뒤로가기 버튼을 누르면 다시 뒤로 갈 수 있게 한다.
import React from "react";
import styled from "styled-components";
import {useHistory} from "react-router-dom";
const NotFound = (props) => {
const history = useHistory(); //나 history 갖다쓴다
return (
<>
<h1>주소가 올바르지 않아요!</h1>
<Button onClick={()=>{history.goBack();}}>뒤로 가기</Button>
</>
);
};
const Button = styled.div`
width:200px;
height:50px;
background-color:#e67a65;
border-radius:10px;
font:bold 20px/50px arial;
text-align:center;
color:#fff;
`
export default NotFound;
※ Switch
: 위와 같이 주소가 잘못된 경우 나올 페이지를 지정해줄때 <App.js>에서 Switch문을 사용한다.
import React from "react";
import styled from "styled-components";
import { Route,Switch } from "react-router-dom";
// BucketList 컴포넌트를 import 해옵니다.
// import [컴포넌트 명] from [컴포넌트가 있는 파일경로];
import BucketSecond from "./BucketSecond";
import Detail from "./Detail";
import NotFound from "./NotFound";
function App() {
const [list, setList] = React.useState(["주니어 개발자로 취업하기", "매일 책읽기", "수영 배우기"]);
const text = React.useRef(null);
const addBucketList = () => {
// 스프레드 문법! 기억하고 계신가요? :)
// 원본 배열 list에 새로운 요소를 추가해주었습니다.
setList([...list, text.current.value]);
}
console.log(list);
return (
<div className="App">
<Container>
<Title>내 버킷리스트</Title>
<Line />
{/* 컴포넌트를 넣어줍니다. */}
{/* <컴포넌트 명 [props 명]={넘겨줄 것(리스트, 문자열, 숫자, ...)}/> */}
<Switch>
<Route path="/" exact>
<BucketSecond list ={list} />
</Route>
<Route path="/Detail" component={Detail}>
<Detail />
</Route>
<Route>
<NotFound />
</Route>
</Switch>
</Container>
{/* 인풋박스와 추가하기 버튼을 넣어줬어요. */}
<Input>
<input type="text" ref={text} />
<button onClick={addBucketList}>추가하기</button>
</Input>
</div>
);
}
메인페이지와 서브페이지 이외의 다른 주소로 접근할 경우 NotFound 페이지로 이동하게 한다.
여기서 중요한 점은 NotFound.js를 마지막에 썼기때문에 이 페이지가 뜨는 것이아니라,
Switch문 안에서 NotFound.js 페이지가 path 지정이 안되어있기 때문에 이동이 가능한 것!
만약 Switch문이 없다면 모든페이지에서 NotFound가 나오게 됨
※ 리덕스
: 전역상태를 관리해주는 패키지. 다른 프론트엔드 라이브러리에서도 사용 가능
1. 패키지 설치
yarn add redux react-redux(띄어쓰기로 한번에 여러개의 패키지를 설치할 수 있다)
-> 리덕스를 리액트에서 편하게 쓸 수 있게 도와주는 패키지
2. 리덕스 공식문서 : https://ko.redux.js.org/introduction/getting-started/
Redux 시작하기 | Redux
소개 > 시작하기: Redux를 배우고 사용하기 위한 자료
ko.redux.js.org
3. 상태관리 흐름
어떤 데이터가 있다. state라고 부른다.
이 state를 가져다 쓰는, 수정하려는 컴포넌트들이 꼭 있다.
컴포넌트들이 수정을 요청한다. 나 이거 이렇게 수정해줘!! = action을 dispatch 하다
=> ActionCreator : 액션을 만드는 함수(액션생성함수)
리액트에서도 state는 setState()나, useState() 훅을 써야만 변경이 가능함.
=> 데이터가 마구잡이로 변하지 않도록 불변성을 유지해주기 위함임.
불변성? 허락없이 데이터가 바뀌면 안된단 소리!
리덕스에 저장된 데이터 = 상태 = state는 읽기 전용
가지고 있던 값을 수정하지 않고, 새로운 값을 만들어서 상태를 갈아끼운다.
즉, A에 +1을 할 때, A = A+1이 되는 게 아니고, A' = A+1이라고 새로운 값을 만들고 A를 A'로 바꾼다는 뜻
ex) state=2; ( X )
return 2; (o) => 직접적으로 리듀서를 건드리는 것이 아닌 새로운 값을 반환하는 것만 가능
리듀서에서 실제 수정이 이루어진다
state는 갖고있던 데이터가 바뀌었다. 나랑 연결되어있는 컴포넌트들에게 이 사실을 알려야한다.