๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿ’ฌ/ใ…ใ……ใ…Œใ…‹ใ…ใ…… ์ฑŒ๋ฆฐ์ง€

38์ผ์ฐจ

๐Ÿ™€

38์ผ์ฐจ

 

Part 10. React ๋กœ ์‡ผํ•‘๋ชฐ ๋งŒ๋“ค๊ธฐ (React ๊ธฐ๋ณธ)

Ch 7. Hooks & Context

 


 

 

Ch 6. Hooks & Context

 

 

 ๐Ÿ”— Basic Hooks : ์ปดํฌ๋„ŒํŠธ์˜ state์™€ ๊ด€๋ จ๋œ ๋กœ์ง์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Functional Component != Stateless Componet because state hooks

 

https://ko.reactjs.org/docs/hooks-intro.html

 

 

  • useState: state ๋ฅผ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค.
// ./App.js

...
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;
// ./components/Example1.jsx
// Class Component

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 });
  };
}
// ./component/Example2.jsx
// Functional Component

import React from "react";

export default function Example2() {
  const [count, setCount] = React.useState(0); // Hooks

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={click}>Click me</button>
    </div>
  );

  function click() {
    setCount(count + 1);
  }
}
// ./components/Example3.jsx
// Functional Compnent - Object

import React from "react";

// useState => count
// useState => {count: 0}
export default function Example3() {
  const [state, setState] = React.useState({ count: 0 }); // Hooks

  return (
    <div>
      <p>You clicked {state.count} times</p>
      <button onClick={click}>Click me</button>
    </div>
  );

  function click() {
    setState((state) => ({
      count: state.count + 1,
    }));
  }
}

 

  • useEffect: ๋ผ์ดํ”„ ์‚ฌ์ดํด ํ›…์„ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค. (componentDidMount, componentDidUpdate, componentWillUnmont)

https://rinae.dev/posts/a-complete-guide-to-useeffect-ko

 

[๋ฒˆ์—ญ] useEffect ์™„๋ฒฝ ๊ฐ€์ด๋“œ

Dan Abramov์˜ 'A Complete Guide to useEffect ๋ฒˆ์—ญ'

rinae.dev

// ./App.jsx

...
import Example4 from "./components/Example4";
import Example5 from "./components/Example5";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <Example4 />
        <Example5 />
      </header>
    </div>
  );
}

export default App;
// ./components/Example4.jsx
// Class Component

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("componenDidMount", this.state.count);
  }

  componentDidUpdate() {
    console.log("componentDidUpdate", this.state.count);
  }

  click = () => {
    this.setState({ count: this.state.count + 1 });
  };
}
// ./component/Example5.jsx
// Functional Component

import React from "react";

export default function Example5() {
  const [count, setCount] = React.useState(0); // Hooks

  // ์ตœ์ดˆ
  React.useEffect(() => {
    console.log("componentDidMount");

    return () => {
      // cleanup
      // componentWillUnmount
    };
  }, []);

  // ์ตœ์ดˆ & count๊ฐ€ ์—…๋ฐ์ดํŠธ ๋  ๋•Œ๋งˆ๋‹ค
  React.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);
  }
}

 

  • useContext

 

 

 ๐Ÿ”— Custom Hooks (useSomething) 

 

// ./App.js

...
import useWindowWith from "./hooks/useWindowWidth";

function App() {
  const width = useWindowWith();

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        {width}
      </header>
    </div>
  );
}

export default App;
// ./hooks/useWindowWidth.js

import React, { useEffect } from "react";

export default function useWindowWith() {
  const [width, setWidth] = React.useState(window.innerWidth);

  useEffect(() => {
    const resize = () => {
      setWidth(window.innerWidth);
    };
    window.addEventListener("resize", resize);

    return () => {
      window.removeEventListener("resize", resize);
    };
  }, []);

  return width;
}

 

  • useHasMouted vs withHasMounted
...
import withHasMounted from "./hocs/withHasMounted";
import useHasMounted from "./hooks/useHasMounted";

function App({ hasMounted }) {
  const width = useWindowWith();
  const hasMountedFromHooks = useHasMounted();

  console.log(hasMounted, hasMountedFromHooks);

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
      </header>
    </div>
  );
}

export default withHasMounted(App);
// ./hooks/useHasMounted.js

import { useEffect, useState } from "react";

export default function useHasMounted() {
  const [hasMounted, setHasMounted] = useState(false);

  useEffect(() => {
    setHasMounted(true);
  }, []);

  return hasMounted;
}
// ./hocs/withHasMounted.jsx

import React 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 = `withHasMountder(${Component.name})`;

  return NewComponent;
}

 

 

 ๐Ÿ”— Additional Hooks 

 

render ์‚ฌ์ด์— ์–ด๋–ค ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์ค€๋‹ค.

 

  • useReducer
// ./App.js

import Example6 from "./components/Example6";
import useWindowWith from "./hooks/useWindowWidth";
import withHasMounted from "./hocs/withHasMounted";
import useHasMounted from "./hooks/useHasMounted";

function App({ hasMounted }) {
  const width = useWindowWith();
  const hasMountedFromHooks = useHasMounted();

  console.log(hasMounted, hasMountedFromHooks);

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <Example6 />
      </header>
    </div>
  );
}

export default withHasMounted(App);
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" });
  }
}

 

  • useCallback, useMemo
// ./App.js

import Example7 from "./components/Example6";
import useWindowWith from "./hooks/useWindowWidth";
import withHasMounted from "./hocs/withHasMounted";
import useHasMounted from "./hooks/useHasMounted";

function App({ hasMounted }) {
  const width = useWindowWith();
  const hasMountedFromHooks = useHasMounted();

  console.log(hasMounted, hasMountedFromHooks);

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <Example7 />
      </header>
    </div>
  );
}

export default withHasMounted(App);
// ./components/Example7.jsx

import { useCallback, useMemo, useState } from "react";

function sum(persons) {
  console.log("sum...");
  return persons.map((person) => person.age).reduce((l, r) => l + r, 0);
}

export default function Example7() {
  const [value, setValue] = useState("");
  const [persons] = useState([
    { name: "Mark", age: 39 },
    { name: "Hanna", age: 28 },
  ]);

  const count = useMemo(() => {
    return sum(persons);
  }, [persons]);

  const click = useCallback(() => {
    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);
  }
}

 

  • useRef, useImperativeHandle
// ./App.js

import Example8 from "./components/Example6";
import useWindowWith from "./hooks/useWindowWidth";
import withHasMounted from "./hocs/withHasMounted";
import useHasMounted from "./hooks/useHasMounted";

function App({ hasMounted }) {
  const width = useWindowWith();
  const hasMountedFromHooks = useHasMounted();

  console.log(hasMounted, hasMountedFromHooks);

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <Example8 />
      </header>
    </div>
  );
}

export default withHasMounted(App);
// ./components/Example8.jsx

import { createRef, useRef, useState } from "react";

export default function Example8() {
  const [value, setValue] = useState("");
  const input1Ref = createRef();
  const input2Ref = useRef();

  console.log(input1Ref.current, input2Ref.current);
  return (
    <div>
      <input value={value} onChange={change} />
      <input ref={input1Ref} />
      <input ref={input2Ref} />
    </div>
  );

  function change(e) {
    setValue(e.target.value);
  }
}

 

  • useLayoutEffect
  • useDebugValue

 

 

 ๐Ÿ”— React Router Hooks 

 

https://v5.reactrouter.com/web/api/Hooks

 

Declarative routing for React apps at any scale | React Router

Version 6 of React Router is here! React Router v6 takes the best features from v3, v5, and its sister project, Reach Router, in our smallest and most powerful package yet.

reactrouter.com

 

 

 ๐Ÿ”— ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ํ†ต์‹  

 

  • ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ

// ./App.js

import logo from "./logo.svg";
import "./App.css";
import A from "./components/A";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <A />
      </header>
    </div>
  );
}

export default App;
// ./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>
  );
}

 

  • ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ

// ./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}>ํด๋ฆญ</button>
    </div>
  );

  function click() {
    setValue("A ์˜ ๊ฐ’์„ ๋ณ€๊ฒฝ");
  }
}

 

 

 ๐Ÿ”— ContextAPI : ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ ์ „์ฒด์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๋ฒ•

 

https://ko.reactjs.org/docs/context.html

 

Context – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

// ./App.jsx

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;
  • ๋ฐ์ดํ„ฐ๋ฅผ Set ํ•˜๊ธฐ (๊ฐ€์žฅ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ => ํ”„๋กœ๋ฐ”์ด๋”)
    1. ์ผ๋‹จ ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    2. ์ปจํ…์ŠคํŠธ.ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
    3. value ๋ฅผ ์‚ฌ์šฉ
// ./contexts/PersonContext.js

import React from "react";

const PersonContext = React.createContext();

export default PersonContext;
// ./index.js

import React from "react";
import ReactDOM from "react-dom";
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: "Hanna", age: 28 },
];

ReactDOM.render(
  <React.StrictMode>
    <PersonContext.Provider value={persons}>
      <App />
    </PersonContext.Provider>
  </React.StrictMode>,
  document.getElementById("root")
);

...
  • ๋ฐ์ดํ„ฐ๋ฅผ Get ํ•˜๊ธฐ (1) - Cousumer
    1. ์ปจํ…์Šค๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
    2. ์ปจํ…์ŠคํŠธ.์ปจ์Šˆ๋จธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
    3. value ๋ฅผ ์‚ฌ์šฉ
// ./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>
  );
}
  • ๋ฐ์ดํ„ฐ๋ฅผ Get ํ•˜๊ธฐ (2) - class
    1. static contextType ์— ์ปจํ…์ŠคํŠธ๋ฅผ ์„ค์ •ํ•œ๋‹ค.
    2. this.context => value ์ด๋‹ค.
// ./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;
  • ๋ฐ์ดํ„ฐ๋ฅผ Get ํ•˜๊ธฐ (3) - functional โญ
    • useContext ๋กœ ์ปจํ…์ŠคํŠธ๋ฅผ ์ธ์ž๋กœ ํ˜ธ์ถœํ•œ๋‹ค.
    • useContext ์˜ ๋ฆฌํ„ด์ด value ์ด๋‹ค.
// ./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>
  );
}

 

'๐Ÿ’ฌ > ใ…ใ……ใ…Œใ…‹ใ…ใ…… ์ฑŒ๋ฆฐ์ง€' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

40์ผ์ฐจ  (0) 2022.03.24
39์ผ์ฐจ  (0) 2022.03.24
37์ผ์ฐจ  (0) 2022.03.23
36์ผ์ฐจ  (0) 2022.03.21
35์ผ์ฐจ  (0) 2022.03.21