Hooks are a new addition in React 16.8
npx create-react-app react-hooks-ex
// src/components/Example1.jsx
// 클래스
import React from 'react';
export default class Example1 extends React.Component {
state={count:0};
render(){
const {count}= this.state;
return(
<div>
<p>You clicked {count} times</p>
<button onClick={this.click}>Click me</button>
</div>
);
}
click = () => {
this.setState({count: this.state.count + 1});
}
}
// src/components/Example2.jsx
// 함수
import { React, useState } from 'react';
export default function Example2 () {
const [count, setCount] = useState(0);
return(
<div>
<p>You clicked { count } times</p>
<button onClick={ click }>Click me</button>
</div>
);
function click(){
setCount( count + 1 );
}
}
// src/components/Example3.jsx
// 함수
import { React, useState } from 'react';
// useState => count
// useState => { count: 0 };
export default function Example3 () {
const [state, setState] = useState({ count: 0 });
return(
<div>
<p>You clicked { state.count } times</p>
<button onClick={ click }>Click me</button>
</div>
);
function click(){
// setState({ count: state.count + 1 });
setState((state) => {
return {
count: state.count + 1
};
});
}
// App.js
import logo from './logo.svg';
import './App.css';
import Example1 from './components/Example1';
import Example2 from './components/Example2';
import Example3 from './components/Example3';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Example1 />
<Example2 />
<Example3 />
</header>
</div>
);
}
export default App;
useState
- state 를 대체 할 수 있음
useEffect
- 라이프 사이클 훅을 대체할 수 있음
- componentDidMount
- componentDidUpdate
- componentWillUnmount
// src/components/Example4.jsx
import React from 'react';
export default class Example4 extends React.Component {
state={count:0};
render(){
const {count}= this.state;
return(
<div>
<p>You clicked {count} times</p>
<button onClick={this.click}>Click me</button>
</div>
);
}
componentDidMount(){
console.log("componentDidMount", this.state.count);
}
componentDidUpdate(){
console.log("componentDidUpdate", this.state.count);
}
click = () => {
this.setState({count: this.state.count + 1});
}
}
// src/components/Example5.jsx
import { React, useState, useEffect } from 'react';
export default function Example5 () {
const [count, setCount] = useState(0);
useEffect(()=>{
// console.log("componentDidMount", count);
console.log("componentDidMount");
return () => {
// cleanup
// componentWillUnmount
};
}, []); // 비워두면 렌더가 계속 됨, []을 넣어주면 최초에만 실행됨
useEffect(()=>{
console.log("componentDidMount & componentDidUpdate by count", count);
return () => {
// cleanup
console.log('cleanup by count', count);
};
}, [count]);
return(
<div>
<p>You clicked { count } times</p>
<button onClick={ click }>Click me</button>
</div>
);
function click(){
setCount( count + 1 );
}
}
// src/hooks/useWindowWidth.js
import { useEffect, useSate } from "react";
export default function useWindowWidth () {
const [width, setWidth] = useSate(window.innerWidth); // 현재 브라우저 해상도
useEffect(()=>{
const resize = () => {
setWidth(window.innerWidth);
};
window.addEventListener("resize", resize);
return () => {
window.removeEventListener('resize', resize);
};
}, []);
return width;
}
// App.js
import logo from './logo.svg';
import './App.css';
import useWindowWidth from './hooks/useWindowWidth';
function App() {
const width = useWindowWidth();
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
{ width }
</header>
</div>
);
}
export default App;
// src/hooks/useHasMounted.js
import { useEffect, useState } from 'react';
export default function useHasMounted () {
const [hasMounted ,setHasMounted] = useState(false);
useEffect(()=>{
setHasMounted(true);
}, []);
return hasMounted;
}
// src/hocs/useWithHasMounted.js
import React from 'react';
export default function WidthHasMounted (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 = `WidthHasMounted(${Component.name})`;
return NewComponent;
}
// App.js
import logo from './logo.svg';
import './App.css';
import WidthHasMounted from './hocs/WidthHasMounted';
import useHasMounted from './hooks/useHasMounted';
// import useWindowWidth from './hooks/useWindowWidth';
function App({hasMounted}) {
const hasMountedFromHooks = useHasMounted();
console.log(hasMounted, hasMountedFromHooks);
// const width = useWindowWidth();
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
{width}
</header>
</div>
);
}
export default WidthHasMounted(App);
useReducer
useCallback, useMemo
useRef,
useImperativeHandle
useLayoutEffect
userDebugValue
useReducer
- 다수의 하윗값을 포함하는 복잡한 정적 로직을 만드는 경우
- 다음 state 가 이전 state 에 의존적인 경우
- Redux 를 안다면 쉽게 사용 가능
// useReducer
// src/components/Example6.jsx
import { useReducer } from "react";
// reducer => state 를 변경하는 로직이 담겨있는 함수
const reducer = (state, action) => {
if(action.type === "PLUS") {
return {
count: state.count + 1
};
}
return state;
};
// dispatch => action 객체를 넣어서 실행
// action => 객체이고 필수 프로퍼티로 type 을 가짐
export default function Example6 () {
const [state, dispatch] = useReducer(reducer, {count: 0});
return(
<div>
<p>You Clicked {state.count} times</p>
<button onClick={click}>Click me</button>
</div>
);
function click(){
dispatch({ type: 'PLUS' })
}
}
// useReducer
// src/components/Example6.jsx
import { useReducer } from "react";
// reducer => state 를 변경하는 로직이 담겨있는 함수
const reducer = (state, action) => {
if(action.type === "PLUS") {
return {
count: state.count + 1
};
}
return state;
};
// dispatch => action 객체를 넣어서 실행
// action => 객체이고 필수 프로퍼티로 type 을 가짐
export default function Example6 () {
const [state, dispatch] = useReducer(reducer, {count: 0});
return(
<div>
<p>You Clicked {state.count} times</p>
<button onClick={click}>Click me</button>
</div>
);
function click(){
dispatch({ type: 'PLUS' })
}
}
// src/components/Example8.jsx
// createRef, useRef
import { useState, createRef, useRef } from "react";
export default function Example8 () {
const [value, setValue] = useState('');
const input1Ref = createRef();
const input2Ref = useRef();
console.log(input1Ref.current, input2Ref.current); // 최초 null undefined, input에 "a"입력 => null <input>
return (
<div>
<input value={value} onChange={change} />
<input ref={input1Ref} />
<input ref={input2Ref} />
</div>
);
function change(e) {
setValue(e.target.value);
}
}
npx create-react-app component-communication
A 의 button 을 클릭하여 E 를 변경하려면?
<A />
컴포넌트에서 button 에 onClick 이벤트를 만들고,- button 을 클릭하면,
<A />
의 state 를 변경하여,<B />
로 내려주는 props 를 변경<B />
의 props 가 변경되면,<C />
의 props 로 전달<C />
의 props 가 변경되면,<D />
의 props 로 전달<D />
의 props 가 변경되면,<E />
의 props 로 전달
// src/components/A.jsx
import { useState } from "react";
export default function A () {
const [value, setValue] = useState('아직 안 바뀜');
return (
<div>
<B value={value}/>
<button onClick={click}>E의 값 바꾸기</button>
</div>
);
function click(){
setValue("E의 값을 변경");
}
}
function B({value}) {
return (
<div>
<p>여긴 B</p>
<C value={value} />
</div>
);
}
function C({value}) {
return (
<div>
<p>여긴 C</p>
<D value={value} />
</div>
);
}
function D({value}) {
return (
<div>
<p>여긴 D</p>
<E value={value} />
</div>
);
}
function E({value}) {
return (
<div>
<p>여긴 E</p>
<h3>{value}</h3>
</div>
);
}
E의 button 을 클릭하여 A 의 P 를 변경하려면?
<A />
에 함수를 만들고, 그 함수 안에 state 를 변경하도록 구현, 그 변경으로 인해 p 안의 내용을 변경.- 만들어진 함수를 props 에 넣어서,
<B />
로 전달<B />
의 props 의 함수를<C />
의 props 로 전달<C />
의 props 의 함수를<D />
의 props 로 전달<D />
의 props 의 함수를<E />
의 props 로 전달,<E />
에서 클릭하면 props 로 받은 함수를 실행
// src/components/A.jsx
import { useState } from "react";
export default function A () {
const [value, setValue] = useState('아직 안 바뀜');
return (
<div>
<p>{value}</p>
<B setValue={setValue} />
</div>
);
}
function B({setValue}) {
return (
<div>
<p>여긴 B</p>
<C setValue={setValue} />
</div>
);
}
function C({setValue}) {
return (
<div>
<p>여긴 C</p>
<D setValue={setValue} />
</div>
);
}
function D({setValue}) {
return (
<div>
<p>여긴 D</p>
<E setValue={setValue} />
</div>
);
}
function E({setValue}) {
return (
<div>
<p>여긴 E</p>
<button onClick={click}>Click</button>
</div>
);
function click(){
setValue('A 의 값을 변경');
}
}
npx create-react-app react-context-ex
React 공식 사이트
context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있습니다.
일반적인 React 애플리케이션에서 데이터는 위에서 아래로 (즉, 부모로부터 자식에게) props를 통해 전달되지만, 애플리케이션 안의 여러 컴포넌트들에 전해줘야 하는 props의 경우 (예를 들면 선호 로케일, UI 테마) 이 과정이 번거로울 수 있습니다. context를 이용하면, 트리 단계마다 명시적으로 props를 넘겨주지 않아도 많은 컴포넌트가 이러한 값을 공유하도록 할 수 있습니다.
// src/contexts/PersonContext.js
import { createContext } from "react";
const PersonContext = createContext();
export default PersonContext;
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import PersonContext from './contexts/PersonContext';
const persons = [
{ id: 0, name: "Mark", age: 39 },
{ id: 1, name: "Jane", age: 28 }
]
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<PersonContext.Provider value={persons}>
<App />
</PersonContext.Provider>
</React.StrictMode>
);
reportWebVitals();
// src/components/Example1.jsx
import PersonContext from "../contexts/PersonContext";
export default function Example1 () {
return (
<PersonContext.Consumer>
{(persons) => {
<ul>
{persons.map((person) => (
<li>{person.name}</li>
))}
</ul>
}}
</PersonContext.Consumer>
);
}
// src/components/Example2.jsx
import React from "react";
import PersonContext from "../contexts/PersonContext";
export default class Example2 extends React.Component {
// static contextType = PersonContext;
render(){
const persons = this.context;
return(
<ul>
{persons.map((person) => (
<li>{person.name}</li>
))}
</ul>
);
}
}
Example2.contextType = PersonContext;
export default App;
(가장 많이 쓰는 방법)
// src/components/Example3.jsx
import { useContext } from "react";
import PersonContext from "../contexts/PersonContext";
export default function Example3 () {
const persons = useContext(PersonContext);
return (
<ul>
{persons.map((person) => (
<li>{person.name}</li>
))}
</ul>
);
}