const [names,setNames] = useState(초깃값)
src/App.js
import React from "react";
import {useState} from 'react';
const heavyWork = () => {
console.log('엄청 무거운 작업의 함수...');
return ['홍길동','김민수'];
}
export default function App() {
const [names,setNames] = useState(() => {
return heavyWork();
});
//heavyWork함수의 리턴값 ['홍길동','김민수']를 초깃값으로 가짐.
const [input,setInput] = useState('');
const handleInputChange = (e) => {
setInput(e.target.value);
};
// input 태그의 value값을 input State의 값으로 변경
const handleUpload = () => {
setNames( (prevState)=> [input, ...prevState] )
}
//input값을 새로 배열의 리스트로 넣어주고, names 배열 갱신
return (
<div>
<input type="text" onChange={handleInputChange}/>
<button onClick={handleUpload}>Upload</button>
{names.map( (name,idx) => {return(
<p key={idx}>{name}</p>
)})}
//map메서드로 반환하는 태그에는 꼭 id값을 key prop으로 명시
</div>
);
}
names스테이트의 '초깃값'
과useState로 추후 변경해줄 값
이
['홍길동','김민수']
->['홍길동','김민수','이영희']
형태처럼
이전state와
밀접하게 연관된 경우
setNames( prevState => [input ...preState] )
와 같이 콜백으로 처리해야한다. ( 불변성을 유지시키기 위해 다른객체를 생성 )
Object.assign
, 배열의경우 concat
메서드로 대체할 수 있으나 콜백함수로 Spread Syntax
를 이용한 값을 리턴하는 것도 괜찮다.
const names =useState(heavyWork())
형태로 useState의 초깃값을 넣어주면 컴포넌트가 리렌더링 될 때마다 무거운 heavyWork함수를 실행시킨다.
따라서 이를 방지하려면 useState에 콜백형태로
useState( ()=> {return( heavyWork() )}; 넣어주면 된다.
useState의 초깃값을 생성하기 위해 무거운 함수를 불러야한다면
=> ( useState( heavyWork() )
컴포넌트함수가 리렌더링 될 때마다 heavyWork함수가 매번 불려지는 문제가 있다.
반면 useState( ()=>{return(heavyWork()} )
와 같이 콜백을 넣어주면 리액트 자체에서 "lazy"하게 맨 처음 렌더링시에만 콜백을 불러준다.
컴포넌트 내부에서 선언되며,
useEffect( callback함수 , [의존배열]);
컴포넌트가 마운트될 때 콜백함수를 호출한다.
1. 의존배열이 없을 때
useEffect( () => { } );
처음 마운트시 & 렌더링 될 때마다(state변동시마다) 실행
2. 의존배열에 값이 있을 때
useEffect( ()=>{ ... } , [value] );
처음 마운트시 & value
값이 바뀔 때 마다 useEffect의 콜백 실행
3. 의존배열이 공배열[] 일때**
useEffect( ()=>{...}, [] );
의존배열로 []공배열
전달시 화면에 처음 마운트
될 때만 실행
[useEffect 예제코드] src/App.js
function App() {
const [count,setCount] = useState(1);
const [name, setName] = useState('');
//useState 초기값은 아무 자료형이나 들어갈 수 있다.
//그리고 한 컴포넌트에서 state는 여러개 선언될 수 있다.
const handleCountUpdate = () => {
setCount(count+1);
};
const handleOnChangeInput = (e) => {
setName(e.target.value)
// console.log(e.target);
// console.log(e.currentTarget);
//e.target: 이벤트 트리거요소 (form속의 submit태그)
//e.currentTarget: event핸들러 설치 엘리먼트
}
useEffect( ()=>{
console.log('처음 마운트시에만 실행..');
},[])
useEffect( ()=>{
console.log('count가 변경될 때마다 실행..');
},[count])
useEffect( ()=>{
console.log('name이 변경될 때마다 실행..');
},[name])
//[의존배열]이 없다면, state변경될 때마다,즉 렌더링때마다 콜백호출실행.
//[변화캐치요소] 속 요소에만 useEffect콜백호출.
//의존배열요소가 있다면? : 마운트 + [ item ] 변경될 때만 실행
//의존배열이 공배열[] : 컴포넌트 처음 마운트시에만 실행
return (
<div>
<button onClick={handleCountUpdate}>Update</button>
<span>Count: {count} </span>
<input type="text" onChange={handleOnChangeInput}></input>
<span>name: {name}</span>
</div>
);
}
export default App;
컴포넌트 언마운트시
Callback의 return값으로 넣어준 콜백함수(clean up함수)
가 clean up
목적으로 호출된다=> setInterval
등의 마운트시 동작한 함수 해제 등에 사용..
//useEffect 콜백의 Return값으로 넣어지는 함수
= clean up 함수
src/component/Timer.js
import React,{useEffect} from 'react';
const Timer = () => {
useEffect( ()=> {
const timer = setInterval( ()=>{
console.log('타이머 돌아가는중..');
},1000)
}, []);
/*첫 마운트시 useEffect의 Callback실행
-> setInterval로 console.log('타이머 돌아가는중..)을 1000ms마다 반복 호출*/
return (
<div>
<span>타이머를 시작합니다, 콘솔을 보세요!</span>
</div>
);
};
export default Timer;
src/App.js
import Timer from './component/Timer';
function App() {
const [showTimer,setShowTimer] =useState(false);
return(
<div>
{showTimer && <Timer/>} //단축평가
/*
[단축평가] &&(and연산자): A && B 모두 참이여야 참(하나라도 거짓이면 거짓)
{ A && <Timer/>(참,보여주려는값))}
- A가 거짓 -> A
- B가 참 -> <Timer/> (B)
*/ <button onClick={()=>setShowTimer(!showTimer)}>Toggle</button>
</div>
);
}
export default Timer;
const ref = useRef(value);
useRef(value)
는{current: value}
인ref객체를
반환한다.
console.log(ref);
//{current : value}
console.log(ref.current)
// value
Ref오브젝트는 얼마든지 수정이가능하기 때문에 언제든 우리가 원하는 값으로 변경해줄 수 있다.
1. Ref값은 컴포넌트 전 생에 주기에 걸쳐 유지된다.
(마운트->언마운트 되기 전까지 유지됨, 즉, 리렌더링 시에도 변하지 않는 특성이 있다.)
2. 반면,함수 및 변수의 경우
컴포넌트 리 렌더링시 새로 호출(함수), 초기화 된다(변수)
리렌더링
은 [ props변화,state변경 ]
등의 이유로 일어남.Ref값
의 변화는 리렌더링을 발생시키지 않는다.//ref는 변화는 감지해야하지만, 그 변화가 렌더링을 발생시키지 않아야 할 떄 유용하다.
ref는 리렌더링시에도 값이 유지되지만(컴포넌트 전 생애주기에 걸쳐 유지)
var는 컴포넌트내부 변수이기 때문에 리렌더링(함수 재호출)시 초기화되므로(다시 선언 및 할당)
[useRef 예제코드] [src/App.js
import React,{ useState , useRef} from 'react';
function App() {
const [count, setCount] = useState(0);
const countRef = useRef(0);
let countVar = 0;
const increaseState = ()=>{
setCount(count + 1);
}
const increaseRef = () =>{
countRef.current += 1;
}
const increaseVar = ()=>{
countVar += 1;
}
return (
<div>
<h1>State,Ref,변수의 컴포넌트 리렌더링시 비교</h1>
<p>State: {count}</p>
<p>//리렌더링시 변함</p>
<p>Ref: {countRef.current}</p>
<p>//리렌더링시 변하지 않지만, ref값은 내부적으로 올라감</p>
<p>Var: {countVar}</p>
<p>리렌더링시 함수컴포넌트가 다시 호출되므로 모든 변수가 0으로 초기화</p>
<button onClick={increaseState}>state올려</button>
<button onClick={increaseRef}>Ref올려</button>
<button onClick={increaseVar}>Var올려</button>
</div>
);
};
export default App;
src/App.js
import React,{useRef,useEffect} from 'react';
const App = () => {
const inputRef = useRef();
useEffect(()=>{
//console.log(inputRef)
inputRef.current.focus();
},[]) //첫 마운트 시에만 콜백호출
const login = ()=>{
alert(`환영합니다 ${inputRef.current.value}!`)
}
return (
<div>
<input ref={inputRef} type="text" placeholder="username"/>
<button onClick={login}>Login</button>
</div>
);
};
export default App;
inputRef
(useRef가 반환한 ref객체)를 input태그의 ref속성으로 넣어주면
//즉,<input ref={inputRef}/>
을 해주면
inputRef.current
값은 input태그가 됨.
//console.log(inputRef) // {current: input}
이를 통해 DOM요소에 접근할 수 있다.
inputRef.current.value === input.value
*/
useMemo( Callback , [의존배열] );
Memoization(메모이제이션), 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술.
useMemo()는 '메모이제이션' 된 'value'를 반환한다.
Callback의 리턴값은 변수 or 함수호출값 이다.
return값
이 무거운 연산이 필요한 함수 호출값(ex.hardCalculate(hardNumber)
)일 경우 useMemo()의 2nd파라미터인 의존배열
로 해당 함수의 호출을 조절할 수 있다.[예제코드1] src/App.js
import './App.css';
import {useMemo, useState} from 'react';
const hardCalculate = (number) => {
console.log('무거운 계산!');
for(let i=0; i<999999999;i++);
return number + 10000;
} //호출시 무거운 연산이 발생
const easyCalculate = (number) => {
console.log('가벼운 계산!');
return number + 1;
}
//함수를 App밖에다 전역으로 선언하면 함수 컴포넌트 리렌더링시 함수 초기화를(불필요한 평가) 막을 수 있음.
function App() {
const [hardNumber, setHardNumber] =useState(1);
const [easyNumber, setEasyNumber] =useState(1);
/* const hardSum = useMemo( ()=> {
return hardCalculate(hardNumber)
},[hardNumber]); */
/* useMemo로 콜백의 return값으로 hardCalculate(hardNumber)
값을 Memoize 시키면 [의존배열]의 hardNumber값이 바뀔 때만
hardCalculate(hardNumber)를 다시 호출해서 hardSum에 할당시켜준다 */
`즉 의존배열의 값이 바뀔때만 hardSum에 리턴값이 재할당됨을 의미.`
/*의존배열이 공배열[]이면 마운트시에만 재할당되므로 이미 그전에 갖고있던
hardSum의 값을 재사용한다.*/
//=>useMemo는 콜백의 리턴값 캐싱을 통해 불필요한 연산을 줄이는 최적화과정.
// 의존배열의 값이 바뀔때만 useMemo콜백의 연산을 다시해서 새로운 리턴값을 할당.
// const hardSum = hardCalculate(hardNumber);
const hardSum = useMemo(
()=> {
return(hardCalculate(hardNumber))
},[hardNumber]);
//hardNumber가 바뀔때만 호출하므로 easyNumber의 변경은
//hardCalculate를 다시 호출하지 않는다.
const easySum = easyCalculate(easyNumber);
return (
<div>
<h3>어려운 계산기</h3>
<input
type="number"
value={hardNumber}
onChange={(e)=> setHardNumber(parseInt(
e.target.value))}
/>
<span> +10000 = {hardSum}</span>
<h3>쉬운 계산기</h3>
<input
type="number"
value={easyNumber}
onChange={(e)=> setEasyNumber(parseInt(
e.target.value))}
/>
<span> +1 = {easySum}</span>
</div>
);
}
export default App;
[예제코드2 😀 ] src/App.js
import React,{useState,useMemo,useEffect} from 'react';
const App = () => {
const [number,setNumber] =useState(0);
const [isKorea,setIsKorea] =useState(true);
// const location = isKorea? 'Korea' :'Abroad'
// 버튼클릭에 따라 isKorea 불리언값 바뀌고 location값이 바뀜
const location =useMemo( () => ({country: isKorea ? "Korea":"Abroad"}) ,[isKorea]);
//첫 렌더링시 & isKorea값이 바뀔때만 새로운 객체를 리턴해서 할당
/* 따라서 number값의 변경에 대해서는 location은 새로운 값을 할당받지않고
(useMemo가 호출되지 않음) useEffect역시 location이 캐싱된값을 유지하므로 호출되지않음!*/
useEffect(()=>{
console.log('useEffect호출');
},[location]);
//=>처음렌더링 & location값이 변경될 때마다 useEffect호출->콜백호출
/*만약 location값이 원시형(str,num,bool,null,undef,bigint,symbol)
이 아니라 객체형 (원시타입제외: Object,array 등...) 이면
number state변경시 컴포넌트 리렌더링 -> 함수컴포넌트 재호출
-> location 변수도 매번 함수재호출 때마다 새로운 객체주소값을 할당받아 매번 다른 객체가 된다.
cf).location객체는 크기가 커서 특정 메모리 주소에 저장되고
변수에는 해당 메모리주소를 참조할 수있는 메모리 주소값이 저장됨 */
return (
<div>
<h2>하루 몇 끼 먹나요?</h2>
<input
type="number"
value={number}
onChange={(e)=>{setNumber(e.target.value)}}
/>
<hr></hr>
<h2>어느 나라에 있나요?</h2>
<p>나라: {location.country} </p>
{/* location식별자는 객체의 참조 주소값이므로 .country로 명시해줘야함 */}
<button onClick={()=>setIsKorea(!isKorea)}>비행기타요</button>
</div>
);
};
export default App;
useCallback( Callback, [의존배열]);
Memoized된 Callback
을 리턴한다.내부변수값 역시 캐싱
되므로[예제코드1]src/App.js
import './App.css';
import {useEffect,useState,useCallback} from 'react';
function App() {
const [number,setNumber] =useState(0);
const [toggle,setToggle] =useState(true);
const someFunction = useCallback( ()=>{
console.log(`someFunc: number:${number}`);
}, [number]);
/*number값이 바뀌어야만 callback을 할당시킨다
number가 안바뀌면 number state값이 바뀌어도
캐싱된 시점의 number변수값으로 할당된다.(함수자체를 기억)*/
useEffect( ()=>{
console.log('someFunction이 변경되었습니다.');
},[someFunction]);
/* number변경=>useCallback호출=>memoized된 함수 리턴
=>someFunction변경 =>useEffect호출 => useEffect콜백실행=>
=> 'someFunction이 변경되었습니다.'*/
return (
<div>
<input
type="number"
value={number}
onChange={(e)=>setNumber(e.target.value)}
/>
<button onClick={()=>setToggle(!toggle)}>{toggle.toString()}</button>
{/*toggle state값이 변해도 의존배열[number]값은 변하지 않으므로
캐싱된 someFunction 콜백은 유지된다.
따라서 someFunction 변경문구가 콘솔에 뜨지 않는다.*/}
<br></br>
<button onClick={someFunction}>Call someFunction</button>
</div>
);
}
export default App;
[예제코드2] src/App.js
import React,{useState,useCallback}from 'react';
import Box from './Box';
const App = () => {
const [size,setSizes] = useState(100);
const [isDark,setIsDark] =useState(false);
const createBoxStyle = useCallback(()=>{
return {
backgroundColor: 'powderblue',
width:`${size}px`,
height:`${size}px`,
}
},[size]);
//props로전달할 CSS객체를 생성하는 함수
//isDark값 변하면 리렌더링->함수초기화->createBoxStyle값 변경(주소값변경)
//이를 극복하기 위해 useCallback으로 감싸줌.
//size값이 변경되지 않는이상 memoized된 콜백을 캐싱해서 사용하겠다.
// *초기화?? : 코드평가시 암묵적으로 할당된 undefined값에 새로운값을 할당시키는작업.
return (
<div
style={ {background: isDark? 'black':'white'}}>
<input
type="number"
value={size}
onChange={(e)=>{setSizes(e.target.value)}}
/>
<button onClick={()=>setIsDark(!isDark)}>Change Theme</button>
<Box createBoxStyle={createBoxStyle}/>
{/* Box컴포넌트에 CSS객체를 props로 전달 */}
</div>
);
};
export default App;
src/Box.js
import React, {useEffect,useState} from 'react';
import App from './App';
const Box =({createBoxStyle}) => {
const [style,setStyle] = useState({});
useEffect( ()=>{
console.log('박스키우기🐶');
setStyle(createBoxStyle());
},[createBoxStyle] );
//input.value 를 키울때마다 console에 '박스키우기🐶'가 찍히는 이유?
/* [동작원리]
Input.value값을 키우면 sizes 스테이트가 변경돼서
App을 다시호출 -> createBoxStyle 다시 초기화(주소값변경->createBoxStyle값 변경)
콘솔에 박스키우기 log출력&style state변경=>div박스 변화 */
return <div style={style}></div>
//style적용한 div렌더링
};
export default Box;
(feat. useMemo, useCallback)
React.memo
?
( HOC: High-Order Components, 고차컴포넌트 )
어떤 컴포넌트를 받아 새로운 컴포넌트(최적화된
)를 반환해주는 컴포넌트
원리: <Students/> -> (프롭체크) -> <Students/>(optimized)
//리렌더링or재사용 여부를 프롭체크를 통해서만 결정함
React.memo
는 어떤 경우에 사용하는가?같은 props
로 자주 렌더링 될 떄렌더링 될 때마다 복잡한 로직
을 처리해야 한다면?src/App.js
import {useState} from 'react'
import Child from './Child';
function App() {
const [parentAge, setParentAge] =useState(0);
const [childAge,setChildAge] =useState(0);
const incrementParentAge = () =>{
setParentAge(parentAge +1);
//state가 num원시형 -> 불변성 상관없음
}
const incrementChildAge = () =>{
setChildAge(childAge +1);}
console.log('부모가 렌더링 되었어요');
return (
<div style={ {border:'2px solid navy',padding:'10px'}}>
{/* 인라인스타일, style={css객체}, 속성:'요소값'
css: .id1 { border: 1px solid red;} */}
<h1 style={{border:'1px solid powderblue'}}>부모👨👩👧</h1>
<p>age:{parentAge}</p>
<button onClick={incrementParentAge}>부모 나이 증가</button>
<button onClick={incrementChildAge}>자녀 나이 증가</button>
<hr></hr>
<Child name={'홍길동'} age={childAge}></Child>
{/* 변하지않는 name프롭스와,변하는 Age props전달 */}
</div>
);
}
export default App;
//부모컴포넌틀 렌더링(state변화) -> 자녀컴포넌트 렌더링
src/Child.js
import React ,{memo} from 'react';
/*Child컴포넌트는 props인자가 변경되지 않는다면
굳이 렌더링시켜줄 필요가 없는 컴포넌트이다(ui변경점이 없기때문)*/
const Child = ({name,age}) => {
console.log('자녀도 렌더링이 되었네요');
return (
<div style={{border:'2px solid powderblue', padding:'10px',}}>
<h3>자녀👼</h3>
<p>name: {name} </p>
<p>age: {age}</p>
</div>
);
};
export default memo(Child);
//name,age프롭스가 변경되지않는다면 렌더링을 막아주는 최적화
//React.memo : export defalt memo(최적화시킬 컴포넌트)
//고차컴포넌트~ 하나의 함수, 컴포넌트를 인자로 받아 새로운 컴포넌트 리턴.
- 객체의 특성에 답이 있다.
객체는 함수컴포넌트가 재호출될 경우 객체값이 아닌 새로운 주소값을 할당받는다.
즉, const obj1= {lastName:'hong',firstName:'gildong'} 형태로 선언한 객체의 식별자엔 리렌더링시마다 새로운 주소값이 계속해서 할당된다.
cf.)함수역시 '객체'이므로 같은 문제가 발생한다!
[예제코드1 React.memo + useMemo]
src/App.js
import {useState} from 'react'
import Child from './Child';
function App() {
const [parentAge, setParentAge] =useState(0);
const incrementParentAge = () =>{
setParentAge(parentAge +1);
//state가 num원시형 -> 불변성 상관없음
}
/*const name = {
lastName:'hong',
firstName:'gildong',
}*/
// App컴포넌트 렌더링시마다 계속 새로운 객체(주소)값 할당
const name = useMemo( ()=> {
return({
lastName:'hong',
firstName:'gildong',
}, [] ); //첫 마운트시에 캐싱후 저장값 사용, 변하지않음.
}
return (
<div style={ {border:'2px solid navy',padding:'10px'}}>
<h1 style={{border:'1px solid powderblue'}}>부모👨👩👧</h1>
<p>age:{parentAge}</p>
<button onClick={incrementParentAge}>부모 나이 증가</button>
<hr></hr>
<Child name={'홍길동'} name={name} ></Child>
{/* 변하지않는 name프롭스와,변하는 Age props전달 */}
</div>
);
}
export default App;
src/Child.js
import React ,{memo} from 'react';
const Child = ({name}) => {
console.log('자녀도 렌더링이 되었네요');
return (
<div style={{border:'2px solid powderblue', padding:'10px',}}>
<h3>자녀👼</h3>
<p>name: {name}</p>
</div>
);
};
export default memo(Child);
//Child에 React.memo적용 props변화여부로 리렌더링 결정
=>
useMemo훅
으로 App에서name
값을캐싱
해주었기 때문에React.memo
를 적용한 Child는 props변화가 없어서 리렌더링되지않는다.
=>useMemo훅을 적용하지 않은 name값은Child의 리렌더링을 유도한다
.
[예제코드2: React.memo + useCallback]
src/App.js
import {useState} from 'react'
import Child from './Child';
function App() {
const [parentAge, setParentAge] =useState(0);
const incrementParentAge = () =>{
setParentAge(parentAge +1);
}
/*const tellMe = () => {
console.log('길동아 사랑해!');
}// 리렌더링시마다 변하는 tellMe함수(객체) ->자녀컴포넌트의 리렌더링 유도*/
const tellMe = useCallback( ()=>{
console.log('길동아 사랑해!~);
},[]) //첫 마운트시 함수 캐싱후 재사용
//컴포넌트가 리렌더링돼도 tellMe함수는 캐싱된 함수로 재활용한다.
return (
<div style={ {border:'2px solid navy',padding:'10px'}}>
<h1 style={{border:'1px solid powderblue'}}>부모👨👩👧</h1>
<p>age:{parentAge}</p>
<button onClick={incrementParentAge}>부모 나이 증가</button>
<hr></hr>
<Child name={'홍길동'} tellMe={tellMe} ></Child>
</div>
);
}
export default App;
src/Child.js
import React ,{memo} from 'react';
const Child = ({tellMe}) => {
console.log('자녀도 렌더링이 되었네요');
return (
<div style={{border:'2px solid powderblue', padding:'10px',}}>
<h3>자녀👼</h3>
<p>name: {name} </p>
</div>
);
};
export default memo(Child);
React.memo
를 적용해 props체크를 하는Child컴포넌트는
App에서useCallback
으로 캐싱한tellMe함수
를 props으로 받아
App이재호출되어도 리렌더링되지않는다.
const [ state_name ,dispatch ] = useReducer(reducer,initialState);
useReducer
는 reducer함수
와 초기state값인 initialState
를 인자로 받아 initialState는 state_name에 할당하고, dispatch함수를 dispatch 식별자에 할당한다.
- useReducer 동작원리
dispatch(action) -> reducer(state,action)호출 -> state리턴(갱신)
배열고차함수
- Arr.map( (item,idx,thisArr) => {})
콜백의 return값으로 새로운 배열을 만들어 반환- Arr.filter( (item,idx,thisArr) => {item.id !== id} )
콜백의 return값이 true 아이템만을 요소로하는 새로운 배열을 반환
src/App.js
import React, { useState, useReducer } from 'react';
import Student from './Student';
const reducer = (state, action) => {
switch (action.type) {
case 'add-student':
const name = action.payload.name;
const newStudent = {
id: Date.now(),
name,
isHere: false,
};
return {
count: state.count + 1,
students: [...state.students, newStudent],
};
case 'delete-student':
return{
count: state.count -1,
students: state.students.filter( student => student.id !==action.payload.id)
}
case 'mark-line':
return {
count: state.count,
students: state.students.map(
(student)=> {
if(student.id === action.payload.id){
return {...student, isHere: !student.isHere}
}
return student;
}
),
}
default:
return state;
}
};
const initialState = {
count: 0,
students: [],
};
const App = ()=> {
const [name, setName] = useState('');
const [studentsInfo, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h1>출석부</h1>
<p>총 학생 수 : {studentsInfo.count}</p>
<input
type="text"
value={name}
placeholder="이름을 입력해 주세요 "
onChange={((e) => setName(e.target.value))}
></input>
<button onClick={()=>dispatch({ type: 'add-student', payload: {name} })}>
추가
</button>
{studentsInfo.students.map((student) => {
return (<Student
dispatch={dispatch}
isHere={student.isHere}
key={student.id}
name={student.name}
id={student.id}
/>
);
})}
</div>
/*이벤트핸들러에 onClick={dispatch(action)}으로넣으면 안되는이유!
함수호출값으로 넣어버리면 해당 값을 계산하기위해 dispatch(action)을 호출해버리고
=>state변경=>App컴포넌트 리렌더링=> dispatch(action)다시호출 무한렌더링발생,
이벤트핸들러는 화살표함수 형태로 ()=>{dispatch(action)} 넣어준다. */
);
}
export default App;
src/Student.js
import React from 'react';
const Student = ({dispatch,name,id,isHere }) => {
return (
<div>
<span
style={{
textDecoration: isHere ? 'line-through':'none',
color: isHere ? 'gray':'black'}}
onClick={()=>{
dispatch({type:'mark-line',payload: {id}})
}}
>{name}</span>
<button
onClick={() => {
dispatch({ type: 'delete-student', payload: {id} });
}}
> 삭제
</button>
</div>
);
};
export default Student;
[별코딩 React Hooks 쉽게 마스터하기]