import React, { useState } from 'react'; // import 구문을 통해 불러오기
const [value, setValue] = useState('초기값');
// 비구조화 할당으로 useState() 함수의 리턴값을 변수에 저장
this.state
가 제공하는 기능과 똑같다. 일반적으로 일반 변수는 함수가 끝날 때 사라지지만, state 변수는 React에 의해 사라지지 않는다. 변수 명은 개발자가 원하는대로 지어도 된다.useState()
에서 초깃값의 형태는 자유다.useState()
는 길이가 2인 배열을 반환한다. 배열의 첫 번째 원소는 현재 상태이고, 두 번째 원소는 상태를 설정하는 함수(setter)다.useState()
예제import React, { useState } from 'react';
const Info = () => {
// useState()를 여러개 사용할 경우
const [ name, setName ] = useState('');
const [ number, setNumber ] = useState('');
const changeName = e => {
setName(e.target.value);
}
const changeNumber = e => {
setNumber(e.target.value);
}
return (
<div>
<input value={name} onChange={changeName}/>
<input value={number} onChange={changeNumber}/>
<h1>{name}</h1>
<h1>{number}</h1>
</div>
);
};
export default Info;
import React, { useEffect } from 'react';
useEffect(effects[, deps]);
useEffect()
는 함수 컴포넌트 내에서 이런 side effects를 수행할 수 있게 해준다. class 컴포넌트의 componentDidMount
나 componentDidUpdate
, componentWillUnmount
와 같은 목적으로 제공되지만, 하나의 API로 통합된 것이다.💡 Side Effect란?
일반적으로 side effect는 부작용, 의도하지 않는 결과를 의미한다. 컴퓨터 공학에서의 side effect란 함수의 로컬 상태를 함수 외부에서 변경하는 것을 말한다. 함수를 입력값에 대해 일정한 출력을 하는 것으로 가정할 때, 출력값에 영향을 미치지 않는 모든 작업들, 예로들어 로그찍기, 네트워크 통신, API 호출 등을 side effect라고 할 수 있다.
이 개념을 react에 적용하면 컴포넌트 안에서 데이터를 가져오거나 구독하고, DOM을 직접 조작하는 작업을 “side effects”(또는 짧게 “effects”)라고 할 수 있다. 왜냐하면 이러한 작업들은 다른 컴포넌트에 영향을 줄 수도 있고, 렌더링 과정에서는 구현할 수 없는 작업이기 때문이다.
useEffect()
는 effects 함수를 첫 번째 매개변수로 전달받아, 컴포넌트가 렌더링 될 때마다 effects 함수를 실행한다.useState()
와 같이 중복 사용이 가능하다.useEffect()
는 두 번째 매개변수로 배열을 받는다. 이 배열에는 useEffect()
함수가 변화를 감지할 state를 요소로 넣는다.useEffect()
는 기본적으로 렌더링되고 난 직후마다 실행되며, 두 번째 매개변수 배열에 무엇을 넣는지에 따라 실행되는 조건이 달라진다. 컴포넌트가 언마운트 되기 전이나 업데이트 직전에 어떠한 작업을 수행하고 싶다면 useEffect()
함수에서 뒷정리(clean-up) 함수를 반환해야 한다.useEffect()
사용 예import React, { useState, useEffect } from 'react';
const Info = () => {
const [ name, setName ] = useState('');
const changeName = e => {
setName(e.target.value);
}
useEffect(() => {
console.log('effct!');
console.log(`name : ${name}`);
return () => { // 언마운트전, 업데이트전만 실행
console.log('clean');
console.log(`name : ${name}`); // 업데이트 직전 값에 접근
}
}, [name]) // name이 변경될 때만 effects 함수 실행
return (
<div>
<input value={name} onChange={changeName}/>
<h1>{name}</h1>
</div>
);
};
export default Info;
props
로 받은 값을 컴포넌트의 로컬 상태로 설정import React, { useReducer } from 'react';
const [state, dispatch] = useReducer(reducer, initialArg);
useState()
의 확장형 대체 함수로, 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트 할 때 주로 사용하는 Hook.(state, action) => newState
useReducer()
함수의 첫 번째 매개변수. 현재 상태(state)와 업데이트를 위해 필요한 정보(action)을 매개변수로 전달받아 새로운 상태를 반환하는 함수.dispatch(action);
import React, { useState, useEffect } from 'react';
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
export default Counter;
import React, { useState, useEffect, useReducer } from 'react';
function reducer (state, action) {
return {
...state,
[action.name]: action.value
}
}
const Info = () => {
const [state, dispatch] = useReducer(reducer, {
name: '',
number: ''
})
const changeValue = (e) => {
dispatch(e.target)
}
return (
<div>
<input name="name" value={state.name} onChange={changeValue}/>
<input name="number" value={state.number} onChange={changeValue}/>
<h1>{state.name}</h1>
<h1>{state.number}</h1>
</div>
);
};
export default Info;
import React, { useMemo } from 'react';
const memoizedValue = useMemo(() => fn(a, b), [a, b]);
useMemo()
는 메모이제이션된 값을 반환하여 동일 계산의 반복 수행을 최소화하기 위한 Hook이다. useMome()
는 특정 데이터가 변경 되었을 때에만 매개변수로 전달받은 함수를 실행한다. 이 최적화는 모든 렌더링 시의 고비용 계산을 방지하게 해준다.useMemo()
에 전달된 함수는 렌더링 중에 실행되며, 사이드 이펙트(DOM조작, 데이터 불러오기 등)같이 렌더링 중에 할 수 없는 작업들은 함수 내에 작성하지 않는다. (사이드 이펙트를 다루는 작업은 useEffect에서 해야한다.)useMemo()
는 렌더링하는 과정에서 특정 값이 변경되었을 때만 연산을 실행하고, 값이 변경되지 않았다면 이전에 연산했던 결과를 다시 사용한다.useMemo()
를 사용할 수 있다. 리액트는 useMemo()
의 메모이제이션 방식을 변경할 수도 있기 때문에, useMemo()
를 사용하지 않고도 동작할 수 있도록 코드를 작성하고, 작성된 코드를 추가하여 성능을 최적화 할 것을 권장하고 있다.useMemo()
는 첫 번째 매개변수로 값이 변경 될 때마다 실행될 함수를, 두 번째 매개변수로는 배열을 갖는다. 이 배열에는 어떤 state가 변경되었을 때 첫 번째 매개변수를 실행할 지를 명시해야한다.(리액트에서는 이를 의존성 값이라고 한다.) 두 번째 매개변수가 없는 경우 매 렌더링 때마다 새 값을 계산한다.fn()
함수가 실행된다.import React, { useState, useMemo} from 'react';
const getAverage = numbers => {
console.log('평균값 계산중');
const length = numbers.length;
if(length == 0) return;
const sum = numbers.reduce((a, b) => a + b);
return sum/length;
}
const Info = () => {
const [ list, setList ] = useState([]);
const [ number, setNumber ] = useState('');
const changeValue = (e) => {
setNumber(e.target.value);
}
const addList = () => {
const nextList = [...list, parseInt(number)];
setList(nextList);
setNumber('');
}
const avg = useMemo(() => getAverage(list), [list])
return (
<div>
<input name="number" value={number} onChange={changeValue}/>
<button onClick={addList}>추가</button>
<ul>
{
list.map((number, index) =>
<li key={index}>{number}</li>
)
}
</ul>
<div>평균값 : {avg}</div>
</div>
);
};
export default Info;
import React, { useCallback } from 'react';
const memoizedCallback = useCallback(() => fn(a, b), [a, b]);
useCallback()
이다. 즉 컴포넌트의 props로 넘겨주는 함수는 useCallback 사용해야 최적화가 된다.useMemo()
는 메모이제이션된 값을 반환하여 동일 계산 반복수행을 최소화해 최적화를 한다면, useCallback()
는 메모이제이션된 콜백을 반환하여 새로운 함수가 생성되는 것을 줄여 최적화를 한다.useCallback()
은 최초에 혹은 특정 조건에서 생성한 함수의 참조를 기억하여 반환해주는 hook이다. 새로 생성되지 않는다함은 메모리에 새로 할당되지 않고 동일 참조 값을 사용하게 된다는 것을 의미하고, 이는 최적화된 하위 컴포넌트에서 불필요한 렌더링을 줄일 수 있다는 것을 뜻한다.const Parent = () => {
return (
<>
<Child onClick={() => console.log('callback')}/>
<Child onClick={() => console.log('callback')}/>
<Child onClick={() => console.log('callback')}/>
<Child onClick={() => console.log('callback')}/>
<Child onClick={() => console.log('callback')}/>
... // 똑같은 함수가 리랜더링 될 때마다 계속해서 재 생성된다.
</>
);
};
// 렌더되는 Child 컴포넌트의 수 만큼 인라인 함수가 생성된다.
const Child = ({onClick}) => {
return <button onClick={onClick}>Click Me!</button>
};
const Parent = () => {
const _onClick = () => {
console.log('callback');
};
return (
<>
<Child onClick={_onClick}/>
<Child onClick={_onClick}/>
<Child onClick={_onClick}/>
... // 로컬 함수인 _onClick()을 하위 컴포넌트가 참조하고 있다.
</>
);
};
// Child이 여러 번 생성되더라도 onClick props으로 전달되는 _onClick 함수는 한번만 생성된다.
const Child = ({onClick}) => {
return <button onClick={onClick}>Click Me!</button>
};
_onClick()
함수가 새로 생성되는 것이다.const Root = () => {
const [isClicked, setIsClicked] = useState(false);
const _onClick = useCallback(() => {
setIsClicked(true);
}, []);
// dependency가 없으므로 Root component가 렌더링 되는 최초에 한번만 생성되며
// 이후에는 동일한 참조 값을 사용한다.
return (
<>
<Child onClick={_onClick}/>
<Child onClick={_onClick}/>
<Child onClick={_onClick}/>
...
</>
);
};
// Root와 Child가 여러번 렌더링 되더라도 onClick props으로 전달되는 _onClick 함수는
// 한번만 생성되므로 계속해서 동일 참조 값을 가진다.
const Child = ({onClick}) => {
return <button onClick={onClick}>Click Me!</button>
};
import React, { useState, useMemo, useCallback} from 'react';
const getAverage = numbers => {
console.log('평균값 계산중');
const length = numbers.length;
if(length == 0) return;
const sum = numbers.reduce((a, b) => a + b);
return sum/length;
}
const Info = () => {
const [ list, setList ] = useState([]);
const [ number, setNumber ] = useState('');
const changeValue = useCallback((e)=>{
setNumber(e.target.value);
}, [])
// 기존 값을 조회하지 않고 설정만하기 때문에
// 해당 컴포넌트가 처음 렌더링 될 때 생성된 함수를 사용
const addList = useCallback(()=>{
const nextList = [...list, parseInt(number)];
setList(nextList);
setNumber('');
}, [number, list])
// number, list를 조회하여 새로운 배열을 생성하기 때문에
// 의존성 값에 number와 list를 추가한다.
const avg = useMemo(() => getAverage(list), [list])
return (
<div>
<input name="number" value={number} onChange={changeValue}/>
<button onClick={addList}>추가</button>
<ul>
{
list.map((number, index) =>
<li key={index}>{number}</li>
)
}
</ul>
<div>평균값 : {avg}</div>
</div>
);
};
export default Info;
import React, { useRef } from 'react';
const refContainer = useRef(initialValue);
useRef()
Hook은 함수형 컴포넌트에서 ref를 쉽게 사용할 수 있게 해준다.React.createRef()
로 ref 객체를 생성하고 해당 객체의 current 프로퍼티를 이용해 해당 DOM을 조작하는 방식과 비슷하다.this.myRef = React.createRef(); // ref 생성
return <div ref={this.myRef} /> // ref 어트리뷰트를 통해 React 엘리먼트에 부착
this.myRef.current // current 프로퍼티로 DOM 조작
function TextInputWithFocusButton() {
const inputEl = useRef(null); // ref 객체 생성
const onButtonClick = () => {
// `current` 프로퍼티는 input을 가리킨다.
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
{ // ref 어트리뷰트를 통해 React 엘리먼트에 부착 }
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
useRef()
Hook 은 DOM 을 선택하는 용도 외에도, 컴포넌트 안에서 조회 및 수정 할 수 있는 변수를 관리하는 용도로 사용할 수 있다. useRef 로 관리하는 변수는 값이 바뀐다고 해서 컴포넌트가 리렌더링 되지않는다.useRef()
로 관리하고 있는 변수는 설정 후 바로 조회 할 수 있다.useRef()
로 생성된 변수를 사용하여 다음과 같은 값을 관리 할 수 있다.setTimeout
, setInterval
을 통해서 만들어진 id
useRef()
로 리스트 id 생성import React, { useState, useRef } from 'react';
const List = () => {
const [input, setInput] = useState('');
const [items, setItems] = useState([
{id: 1, text: '눈사람'},
{id: 2, text: '얼음'},
{id: 3, text: '눈'},
{id: 4, text: '바람'},
]);
const nextId = useRef(5); // 초기값을 부여해 nextId 생성
const handleInput = e => {
setInput(e.target.value);
}
const itemsList = items.map(name => {
return <li key={name.id}>{name.text}</li>
});
const handleClick = () => {
if(input.trim() === '') return;
const nextItems = items.concat({
id: nextId.current, // id 추가할 때 currnt 프로퍼티로 접근
text: input
});
nextId.current += 1; //id 수정도 current 프로퍼티 사용
// 값을 변경해도 리랜더링 되지 않음
setItems(nextItems);
setInput('');
}
return (
<div>
<input
value={input}
onChange={handleInput}
/>
<button onClick={handleClick}>추가</button>
<ul>
{itemsList}
</ul>
</div>
);
};
export default List;
// src/hooks/useInputs.js
import { useReducer } from 'react';
function reducer(state, action) {
return {
...state,
[action.name]: action.value
}
}
export default function useInputs(initialForm) {
const [state, dispatch] = useReducer(reducer, initialForm);
const onChange = (e) => {
dispatch(e.target);
}
return [state, onChange];
};
// src/Info.js
import React from 'react';
import useInputs from './hooks/useInputs';
const Info = () => {
const [ state, onChange ] = useInputs({
name: '',
number: ''
})
return (
<div>
<input name="name" value={state.name} onChange={onChange}/>
<input name="number" value={state.number} onChange={onChange}/>
<h1>name : {state.name}</h1>
<h1>number : {state.number}</h1>
</div>
);
};
export default Info;
React 공부 중이었는데 정리 잘 하셨네요! 잘 보고 갑니다 :)