코딩/리액트

리액트 / 1주차 강의 노트

민여 2022. 4. 19. 21:49

5일에 걸쳐 작성한 리액트 basic concept !! 

처음엔 understand가 거~의 없었다 

계속 강의 재복습하면서 그냥 감각이 잡힐때까지 반복

MY NOTION

 

 

habit-tracker 앱 복습 

 

최상위 INDEX.JS에서는 APP.JSX를 그리고 
APP.JSX에서는 최종적으로는 컴포넌트로 바꾸고 
NAVBAR와 HABITS 자식 컴포넌트 2개를 그린다 

일단 HABIT이라는 컴포넌트부터 만들고 이 HABIT을 HABITS안에 그려줄건데 

li에 들어갈 부분을 정적으로 html구현을 한다고 생각을 하고 
이름 숫자 플러스버튼 마이너스 버튼 삭제버튼 이렇게 한줄이니까 
habit에서 <span className=해서 이름넣고 등등등 했음 

css까지 스타일 다 잡고나서 플러스버튼 누르면 올라가고 하는 이벤트를 잡을건데 

데이터를 따졌을때 이 한줄li에서 데이터라고 볼수있는건숫자부분 한개임

플러스를 몇번했느냐? ->이 부분을 state 오브젝트로 만들건데 

state = {
 count: 0;


자 이제 그럼 저 숫자 jsx로 만들어낸거에는 일반숫자를 표기하는게 아니라 {}써서 로직으로 나타내야함 
class로 만들었으니까 this.state.count 이렇게 해주고

클릭하면 뭐가 나올려면 onClick이 불려야하니까 
button태그안에 onclick={this.handleIncrement} ->클릭하면handleIncrement라는 콜백함수를 부르라는 거지 

그러면 handleIncrement를 정의하는데 
this.setState({}) 이 함수를 써줘야 state가 변경이 된다 
화살표함수써서 이 함수는 1씩 증가시키는거니까 count: this.state.count + 1 이렇게 

handleDecrement는 마이너스되는거 방지하려면 
const count = this.stae.count -1; 1씩 감소한다는 변수를 지정하고 
this.setState로 제어문 ? : 넣기 -> count: count < 0 ? 0 : count
즉 카운트는 0보다 작으면 0으로 나오고 아니면 그냥 1씩 감소하라는거 

*props속성은 매개변수처럼 컴포넌트 밖에서 정의된거고 
*state는 함수내에서 정의된 변수처럼 컴포넌트 안에서 정의된거 

이렇게하고 이거를 habits라는 컴포넌트를 만들어서 이 안에 그려줄건데 

state에 habits 배열을 만들거
왜냐면 일단 동적으로 눈에보이는 li 3개를 플러스 마이너스 누르면서 동작시킬거 

이름 카운트 해서 배열만들고 
render에 ul 의미있는 태그로 묶어주고 
this.state.habits에 map(배열순회)
이거를 map(habit => ( <Habit /> )) 이렇게 
그니까 Habit 컴포넌트를 li한줄 그린거를 반복할거니까 
각각의 habit이 새로운 배열로 반환이 되는거 
화살표함수는 habit이라는 오브젝트를 Habit컴포넌트로 변환한다는거 

근데 이걸 prop으로 넘겨야 
habit에서 li작성한 이름을 동적으로 나타낼거아니야
map(habit => ( <Habit /> )) 이부분을 
map(habit => ( <Habit  habit={habit} /> )) 
habit은 prop이름 즉 속성명이 되는거지 {}안에들어가는건 전달해줄 데이터 

이렇게 ul 안에 li 
자식요소가 있으면 얘네 고유한 key 아이디값을 주는게중요하다 
이걸 임의로 일단 habits 배열안에 id값 기입하고 
map돌리는거 안에 props 앞에 key={habit.id} 이렇게 해줌 

Habit은 부모에서 받은 속성만 사용하여 보여주는 컴포넌트라
state가 필요없어져서 기입했던거 삭제함 
핸들링로직도 이름만 남겨두고 비워두고
habit 컴포넌트를 콜백함수만 호출해주는 함수로 만들기

이 Habits 컴포넌트안에 Habit 그릴때 
habit을 prop으로 넘긴거 옆에 증가하는 함수를 prop으로 또 넘겨줌

이렇게하고 Habit에도 props 심어줘야하니까 
비워둔 로직안에 this.prop.onIncrement(this.props.habit)

증가는 spread operator로 새 배열껍데기 만들어서
인덱스해서 추가해주고 setstate  

자 이제 왜 app 즉 최상위에 state를 업데이트했느냐
왜 컴포넌트로 바꿔놨느냐 
왜냐면 navnar컴포넌트에서도 state를 써야하기때문이다 
그러면 최상위에 state를 해놓으면 navbar던 habits이던 다 같이 쓸수있으니까 

app을 클래스로 바꿔주고 
Habits에있던 state c+x하고 
핸들링로직들 복사해서 가져오고 
그러면 얘네도 마찬가지로 Habit에 한거처럼
로직들안에 내용을 this.props.onIncrement(habit)으로 전부 바꿔줌 

자 이제 Habits 에 데이터는 없고 
prop에 전달된 습관들 배열을 map 돌면서 
Habit이라는 컴포넌트로 변경해서 보여주는 역할 

굳이 꼽아 말하자면 지금 
Habit 에는 li태그안에 span이렇게 감싸져있고 
Habits에는 ul태그안에 <Habits />해서 프롭들 심어놓음 근데
이거를 {}괄호안에 해서 this.props.habits.map()해준거 

이제 navbar컴포넌트 만들거 
nav안에 로고랑 이름이랑 카운트 
이 카운트 사이에 {}해서 this.props.totalCount
당연히 이거 app가서 navbar 심어준거 옆에 속성으로 심어줘야겠지
totalCount={this.state.habits.filter 하는데
이게 뭐냐면 아이템 카운트가 0이상인 아이들을 골라서
걔네의 length 즉 갯수를 나타내라는거 

이제 addForm만들거 
HabitAddForm이라는 컴포넌트 생성 
form태그로 묶어주고 
안에 input, button 추가

이제 이 폼은 Habits 맨위에 그릴꺼니까 
Habits가서  HabitAddForm 그려줌 

자 이제 다시 폼으로 돌아와서 
이 폼을 누르면 submit이 되면 추가가 되는걸 그릴건데 
이 form태그 안에 onSubmit해주고 위에 변수 작성해줌
submit은 preventdefault해줘야하니까 해주고
이 input에 입력된 데이터를 알아와야지 그려낼수있으니까 
inputRef = React.createRef():해주고 원하는요소에 ref={"this.inputRef"} 전달해줌 
즉 input에. 

자 그러면 onSubmit 에 
이거의 값 즉 this.inputRef.current.value 를 
name 이라는 변수에 할당하고 
이 name이 있다면 props onAdd라는 함수에 이름을 전달해줄거 
->   name && this.props.onAdd(name);

자 우리가 onAdd를 속성명으로 기입한적 없으니 
그려준 곳 Habits에 가서 
HabitAddForm  꺽쇠안에 속성명 onAdd 적어주고 this.handleAdd
그러고 handleAdd 정의해주는데 
this.props.onAdd(name)

이제 app에 그려줘야하는데
Habits에 onAdd 속성명 추가해주고 
여기의 handleAdd정의에는 
name을화살표함수로 받아서 
spread operator 로 새로운 배열껍데기를 만들어줌 
그리고  setstate로 업데이트해줌 
이 배열을 새롭게만들어서 추가해주는거 
그니까 맨첨에 추가할때도 배열로 만들어서 넣었잖아 

내가 인풋에 입력한거 초기화하는거는 
HabitAddForm에가서  this.inputRef.current.value = ''; 막줄에 추가해주거나 
formRef도 createRef 해주고 ref도 form태그 그린거에 심어주고 this.formRef.current.reset();해줘도 됨 

남은건 리셋버튼인데 이건 굳이 컴포넌트 따로 안만들고 Habits안에 태그로 만들거
그 태그안에 onClick되면  this.props.onReset
자 그럼 app 으로와서 
handleReset 정의 
Habits그려놓은 태그안에 
onReset={this.handleReset} 해줌 
handleReset은 map으로 배열을 빙글빙글 돌려서 
카운트를 0으로 만들고 habit을 리턴 
그리고 setState

--------------------------------------------------------------------------------

 

성능 분석을 할건데 렌더함수에 로그 출력해둬서 
렌더가 함수가 언제 호출되는지 확인을 해볼거
지금 인풋에 새로운걸 입력하면 전부다 출력됨 

이건 state가 변해서 자식요소들이 업데이트되니까 
render함수가 호출되는거라 이해감 
근데 플러스버튼이나 그런걸 클릭하면 전부다 호출됨 

근데 리액트는 버추얼돔에서 자기가 실질적으로 업데이트될걸
비교해서 업데이트하는데 그걸 elements에서 깜빡이고 사라지는걸로 확인가능 

근데 이거를 컴포넌츠 툴에서 (숨겨진ㅎㅎ) 설정눌러서 
hightlight updates 체크박스 해주면 화면 자체에서 확인가능 

이렇게 업데이트될거만 할수있게하는게 purecomponent와 memo 
얘네는 state,props에 변화가 없으면 렌더함수 호출x

자 input 이 변화될 일이 없잖아 
HabitAddForm가서 purecomponent로 바꿔주면이제 불필요한 렌더링 하지않음 

 

---------------------------------------------------------------------------------------------


리액트 훅
= 함수형컴포넌트에서도 state, lifecycle 사용가능 

state는 const [count, setCount] = useState(0); (0은 초기값)

 

핸들링이벤트
const handleIncrement = () => {
  setCount(count: count + 1);
};

중요한점은 클래스는 이런 멤버변수들은 클래스가 만들어질때 딱 한번만 만들어지고 
state가 변경되거나 prop이 업데이트되면 render함수만 반복해서 호출이 되는데 
함수형컴포넌트는 함수기때문에 변경이 되면 전부 다 호출이됨 

리액트훅에서는

 

createRef말고 useRef를 사용 
한번만 만들고 메모리에 저장해놓고 다시 재사용

useCallback을 쓰면 동일한 콜백함수만 전달 

useEffect는 
componentDidMount + componentDidUpdate를 결합한 아이

처음 컴포넌트가 업데이트 되었을때만 사용하려면 
useEffect(()=>{
  console.log(~);
}, []);        

카운트가 변경되었을때마다 함수호출 
useEffect(()=>{
  console.log(~);
}, [count]);