동기
- state로 관리하면 재사용하기가 어려웠음. 상태를 this.state를 다른데서 다르게 관리 해야하니까..
- 계층 변화 없이 상태 관련 로직을 재사용할 수 있도록 도와준다.
- 복잡한 컴포넌트 경우 상태관련 로직을 한 공간안에서 묶여있기 때문에 작게 분리하는 것은 불가능 하며, 테스트 하기도 어려움. 또한 상태라이브러리는 많은 추상화, 파일들의 건너뛰기를 요구 하며 재사용하기 어려워졌다.
정리: class의 단점을 보완하면서 / 라이프사이클 등과 관련된 함수를 재사용 가능하도록 한다.
규칙
- 최상위에서만 hook을 호출해야 한다. 반복문, 조건문, 중첩된 함수내에서 hook실행 x
- React함수 컴포넌트 내에서만 hook을 호출해야 한다.
State Hook
일반
import React from 'react';
export default class Test extends React.Component {
state = { count:0 } ;
render() {
const { count } = this.state;
return (
<div>
<p>{count}</p>
<button onClick={this.click}>click me</button>
</div>
)
}
click = () => {
this.setState({count: this.state.count+1 });
}
}
useState 사용
import React from 'react';
//useState >> count
export default function Test2() {
const [count, setCount] = React.useState(0); //() 이 안의 값은 count의 초기값
// 첫번째 인자: 업데이트가 되었을때 변경될 값
// 두번째 인자: count를 바꾸는 함수 >> click 함수를 다시 실행시킨다.
return (
<div>
<p>{count}</p>
<button onClick={click}>click me</button>
</div>
);
function click() {
setCount(count + 1);
}
}
useState 객체로 사용
import React from 'react';
//useState >> { count : 0 } 객체로 사용하기
export default function Test3() {
const [state, setState] = React.useState({count : 0}); //() 이 안의 값은 count의 초기값
// 첫번째 인자: 업데이트가 되었을때 변경될 값
// 두번째 인자: count를 바꾸는 함수 >> click 함수를 다시 실행시킨다.
return (
<div>
<p>{state.count}</p>
<button onClick={click}>click me</button>
</div>
);
function click() {
setState((state) => {
return {
count: state.count + 1
}
});
}
}
setState((state) ....
*중요!! 어떤것을 의존해서 사용하는지? dependency 외부의 state에 의존적이지않고 함수에 넣어두면 인자로 들어오니 의존적이지 않다.
useState를 사용해서 상태 처리하는 이유?
컴포넌트 사이에서 상태 관련된 로직을 재사용하기 어려움
복잡한 컴포넌트 이해하기 어려움
class 는 컴파일 단계에서 코드를 최적화 하기 어렵게 만든다.
this.state 는 render 사이에 레퍼런스를 공유하기 떄문에 문제가 발생할 수 있다. (class컴포넌트)
function컴포넌트는 공유안함
- useState
- useState는 state를 대체할 수 있음
Effect Hook
useEffect
useEffect로 전달된 함수는 지연 이벤트 동안 레이아웃 배치와 그리기를 완료 한 후 발생한다.
vs useLayoutEffect : 수행되는 타이밍이 차이가 난다. → 모든 dom 변경 이후에 동기적으로 발생한다.
useEffet는 라이프 사이클 훅을 대체할 수 있음
- componentDidMount
- componentDidUpdate
- componentWillMount
class 컴포넌트 : state 가 변경되면 didmount, didupdate둘다 변경해줘야 한다 > 중복 코드가 생김
기능
- 데이터 가져오기
- 구독 설정하기
- 수동으로 리액트 컴포넌트의 DOM을 수정하기
React.useEffect(() => {
console.log("componentDidMount");
return () => {
//clean up
//componentWillUnMount
};
}, []); // , [] 는 최초에만 실행한다. 라는 의미
React.useEffect(() => {
console.log("componentDidMount & componentDidUpdate by count");
return () => {
//clean up by count
};
}, [count]); //count가 변했을 때만 실행
- , [] 는 최초에만 실행한다. 라는 의미
- return 하면 clean up 기능이 있음
- 두번째 인자 > 배열.. 실행하는 타이밍 조절
- 항상 렌더 될떄 실행>> 최초에만 실행 등등으로 바꿀 수 있음
Custom Hook
useSomething
화면크기 바뀔때마다 width 바뀌게 만들기
import React, {useState, useEffect} from 'react';
export default function useWindowWidth(){
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const resize = () => {
setWidth(window.innerWidth);
};
window.addEventListener("resize" , resize);
return () => {
window.removeEventListener("resize" , resize);
};
}, []);
return width;
}
useHasMounted vs withHasMounted
App.js
import logo from './logo.svg';
import './App.css';
import Test3 from './components/Test3';
import useWindowWidth from './hooks/useWindowWidth';
import withHasMounted from './hocs/withHasMounted';
function App({ hasMounted }) {
console.log(hasMounted);
const width = useWindowWidth();
const hasMountedFromHooks = useHasMounted();
console.log(hasMounted, hasMountedFromHooks);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Test3/>
{width}
</header>
</div>
);
}
export default withHasMounted(App);
withHasMounted.jsx
import React, {useState, useEffect} from 'react';
export default function withHasMounted(Component){
class NewComponent extends React.Component {
state = {
hasMounted: false,
};
render(){
const { hasMounted } = this.state
return <Component {...this.props} hasMounted = {hasMounted} />
}
componentDidMount() {
this.setState({hasMounted : true});
}
}
NewComponent.displayName = `withHasMounted(${Component.name})`;
return NewComponent;
}
useHasMounted.js
export default function useHasMounted(){
const [hasMounted] = useState(false);
useEffect(() => {
setHasMounted(true);
},[]);
return hasMounted;
}
Additional Hooks
useReducer
- 다수의 하윗값을 포함하는 복잡한 정적 로직을 만드는 경우
- 다음 state가 이전 state에 의존적일 경우
- Redux
import { useReducer } from 'react';
const reducer = (state, action) => {
if(action.type === 'PLUS') {
return {
count: state.count +1,
};
}
return newState
}
export default function Test2() {
const [state, dispatch] = React.useReducer(reducer, {count: 0});
return (
<div>
<p>{state.count}</p>
<button onClick={click}>click me</button>
</div>
);
function click(){
dispatch({type: "PLUS"}) // state의 count를 1 올려준다.
}
}
- reducer: state를 변경하는 로직이 담겨있는 함수
- dispatch: dispatch를 통해서 action객체를 내려준다.
- action : 객체이고 필수 프로퍼티로 type을 가진다.
- PLUS라는 action을 dispatch로 넘겨주어서 action의 type이 해당 값이면 state를 변경한다.
useMemo
import { useState , useMemo} from 'react';
function sum(persons){
return person.map((person) => person.age).reduce((l,r) => l+r , 0)
}
export default function Test2() {
const [value, setValue] = useState('');
const [persons] = usetState([
{ name: "Mark", age : 39 },
{ name: "Hanna", age : 29 }
]);
const count = useMemo(() => {
return sum(persons);
}, [persons]);
//dependency list를 인자로 받는다.
//persons가 변했을 때만 실행하겠다.
//person의 의존적이다.
const click = useCallback(()=> {
//최초의 값만 넣고싶어 .. 계속 갱신 xx 하고 싶다 라면? >>
console.log(value);
}, []); // 이렇게 생성하기 , 이전 함수내용 가지고 있음
return (
<div>
<input value = {value} onChange={change} />
<p>{count}</p>
<button onClick={click}>click</button>
</div>
);
function change(e){
setValue(e.target.value);
}
}
다른 동작을 할때마다 render가 새로 된다. > sum함수가 불필요하게 실행된다. person에 의존적으로 계산이 된다.
person이 같으면 굳이 리렌더링 될 필요 없음 .. 이걸 해결하기 위해 useMemo를 사용한다.
useCallback
최초의 값만 넣고싶어 .. 계속 갱신 xx 하고 싶다 라면? }, []);
이 내용 추가해주면 이전 함수내용 가지고있을 수 있다.
useCallback은 안에 들어있는 함수를 언제 세팅해줄것인지? []
이 dependency list에 의존적으로 결정할 수 있음
언제 사용함??
컴포넌트 최적화할때 사용한다.
useRef
import { useState , useRef ,createRef } from 'react';
export default function Test2() {
const [value, setValue] = useState('');
const inpt1Ref = createRef();
const inpt2Ref = useRef();
console.log(input1Ref.current, input2Ref.current);
return (
<div>
<input value={value} onChange={change} />
<input ref={inpt1Ref}/>
<input ref={inpt2Ref}/>
</div>
);
function change(e){
setValue(e.target.value);
}
}
React Router Hooks
useHistory
props가 들어오는 history랑 같은것을 구해서 사용
import { useHistory } from 'react';
export default function Test2() {
const history = useHistory();
function login(){
setTimeout(()=> {
history.push("/");
});
}
return <button onClick={login}>login</button>
}
useParams
'개발(라이브러리,프레임워크) > react.js & react native' 카테고리의 다른 글
Redux 를 React에 연결(라이브러리x) (0) | 2022.01.21 |
---|---|
Redux 개요, 사용 (0) | 2022.01.20 |
라우팅 (0) | 2021.09.26 |
컴포넌트 (0) | 2021.09.24 |
라이프사이클 (0) | 2021.09.23 |