[KDT]FCFE - 8주3일 React Practice

React

React Practice

HOC

: Higher Order Component ( 고차 컴포넌트 )

HOC = function(컴포넌트) { return 새로운 컴포넌트;}

  • 컴포넌트를 받아 새로운 컴포넌트를 리턴하는 함수 이다.

withRouter(컴포넌트) -> router에 의해 호출되지 않아도 match, location, history 에 접근할 수 있도록 한다.

  • with 가 들어가는 HOC 가 많다.

  • cross-cutting concern ( 횡단 관심사 )

  • Original Component ( Use Composition ) : 그대로 사용하기

  • 컴포넌트를 감싸놓아도 Unrelated Props 를 잘 넘겨주기

  • Easy Debugging : 디버깅이 쉽도록 새롭게 리턴한 컴포넌트에 displayName 표기하기.

주의점

  • render Method 에서 HOC 를 사용해서는 않된다.

  • 인자로 들어간 component의 static Methods는 복사되지 않기 때문에 따로 복사하여 넣어줘야한다. ( 복사해서 넣어주는 라이브러리 : hoist-non-react-statics )

  • Reference는 바로 통과 되지 않는다. ( React.forwardRef 사용 )

Controlled Component & Uncontrolled Component

  • 상태를 갖고 있는 엘리먼트 ( input, select, textarea ... )

Uncontrolled Component

  • react 는 실제 돔에 접근하여 사용한것 보다는 엘리먼트가 갖고 있는 상태를 활용하도록 하는것이 좋다.

inputRef = React.createRef();

  • Ref를 쓰기위해 준비하고

<input ref={this.inputRef}/>

  • Ref를 사용할 태그에 지정해준 후

componentDidMount() {
console.log("componentDidMount", this.inputRef.current.value);
}

  • mount 된 이후 참조되면 사용할 수 있다.

Hooks & Context

  • Basick Hooks

( useState, useEffect, useContext )

React.useState()

const [count, setCount] = React.useState(0);
return (
  <div>
    <p>{count}</p>
    <button onClick={setCount(count+1)}>클릭</button>
  </div>
)
  • 함수형 컴포넌트에서 state를 관리하기 위해서는 hook 을 사용해야한다.

  • useState 훅은 인자로 state 초기값을 전달하고 구조분해 할당으로 useState가 리턴하는 배열의 첫번째는 state의 변수 값을 할당하고 두번째 배열은 state를 변경할 수 있는 method를 할당한다.

const [state, setState] = React.useState({count:0,num:10});
return (
  <div>
    <p>{state.count}</p>
    <button onClick={setState({count: state.count+1})}>클릭</button>
  </div>
)
  • state를 객체로 관리하기위해서는 useState 인자로 객체를 넣고 state.key 로 관리할 수 있다.

  • setState를 사용할 때 인자에 객체의 프로퍼티를 변경하는 방식으로 state를 변경할 수 있다.

const [state, setState] = React.useState({count:0,num:10});
return (
  <div>
    <p>{state.count}</p>
    <button onClick={setState((state)=>{ return { count: state.count + 1}})}>클릭</button>
  </div>
)
  • setState를 사용할 때 인자로 화살표 함수를 사용하여 state 인자를 넣고 객체를 리턴하는 방식을 사용함으로써 의존하지 않고 사용하는 방식을 알아두어야 한다.

useState Hook 을 사용하여 함수형에서 state를 관리하는 이유

  • 컴포넌트 사이에서 상태와 관련된 로직을 재사용하기 어렵다.

  • 복잡한 컴포넌트들은 이해하기 어렵다

  • class는 사람과 기계를 혼동시킨다.

  • this.state는 로직에서 reference를 공유하기 때문에 문제가 발생할 수 있다.

React.useEffect()

  • 컴포넌트의 라이프사이클에 사용할 수 있다.

  • useEffect(첫번째, 두번째)

  • 첫번째 인자에는 동작할 함수를 넣고 두번째 인자는 포커스할 state를 넣는다.

  • 두번째 인자로 넣은 state 값이 변경되면서 rerendering일 일어나면 첫번째 인자에 넣은 함수가 동작한다.

  • 두번째 인자에 아무것도 넣지 않으면 렌더가 일어날때마다 첫번째 인자로 넣은 함수가 동작한다.

  • 두번째 인자에 [] 빈 배열을 넣으면 최초 렌더시에만 첫번째 인자로 넣은 함수가 동작한다.

  • 첫번째 인자의 함수에 리턴값으로 함수를 넣고 리턴된 함수는 첫번째 인자의 함수가 조건에 의해 동작한 후 rerender가 끝나면 즉, update 동작이 끝나면 리턴된 함수가 동작한다.

React.useEffect(()=>{
  console.log("componentDidMount");
}, []);

React.useEffect(()=>{
  console.log("componentDidMount & componentDidUpdate by count",count);
  
  return () => {
    // cleanup
    console.log("cleanup by count", count);
  };
}, [count]);

[https://rinae.dev/posts/a-complete-guide-to-useeffect-ko](useEffect 완벽 가이드)

Custom Hooks

import { useState, useEffect } from 'react';

exprot default function useWindowWidth(){
  const [width, setWidth ] = useState(window.innerWidth);
  
  // mount가 되자마자 dom 을 사용한다.
  useEffect(()=>{
    const resize = () => {
      setWidth(window.innerWidth);
    };
    // resize event 를 걸어 놓는다.
    window.addEventListener("resize", resize);
    
    return () => {
      // 걸어놓은 event를 제거해야한다.
      window.removeEventListener("resize", resize);
    };
  }, []);
  return width;
}

HOC

  • component를 인자로 넣고 component를 리턴한다.

  • component가 랜더된 후나 전에 component에 props를 넣어주거나 변경할 수 있다.

  • props 넣어주는 HOC 만들기

import React from "react";

export default function withHasMounted(Component) {
  class NewComponent extends React.Component {
    state = {
      hasMounted: false,
    };
    render(){
      const { hasMounted } = this.state;
      
      // 리턴값으로 props를 넣은 컴포넌트를 돌려준다.
      // state가 변경되면 리렌더되면서 변경된 props를 넣은 component가 리턴된다.
      return <Component {...this.props} hasMounted={ hasMounted }/>
    }
    
    // Mount 후 hasMounted 값 변경
    componentDidMount(){
      this.setState({ hasMounted: true});
    }
  }
  
  NewComponent.displayName = `withHasMounted(${Component.name})`;
  return NewComponent;
}
import withHasMounted from "./hocs/withHasMounted";

function App({ hasMounted }){
  console.log(hasMounted);
  return (
    <div className="App">
      ...
    </div>
  );
}

export default withHasMounted(App);

// false *초기값
// true *mount 후 변경값
// HOC 가 적용되어 두번 출력됨.

Additional Hooks

  • useReducer
import { useReducer } from "react";

const reducer = (state, action) => {
  if (action.type === "PLUS") {
    return { count2: state.count2 + 1 };
  }
  return state;
};

const MyButton = () => {
  const [state, dispatch] = useReducer(reducer, { count2: 0 });
  console.log("rendering");
  
  function onClick() {
    dispatch({ type: "PLUS" });
  }

  return (
    <div>
      <button onClick={onClick}>{state.count2}</button>
    </div>
  );
};

export default MyButton;
  • useMemo

useEffect 와 비슷하게 의존하는 두번째 인자가 변경될때 실행 되도록 한다.

  • useCallback

첫번째 인자로 넣은 함수를 변경하는 시점을 의존적으로 사용한다.
즉, 두번째 인자로 넣은 값이 변경될때 첫번째 인자로 넣은 함수도 변경된다.

import { useState, useMemo } from "react";

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

export default function Example7(){
  const [value, setValue] = useState("");
  const [persons] = useState([
    { name: "Mark", age: 39 },
    { name: "Mark", age: 39 },  
  ]);
  
  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

render 사이에 유지를 해주도록 한다.

함수형 컴포넌트는 전부 새로 랜더되므로 랜더시 유지해주는 훅이 존재한다.

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

export default function Example8 () {

  const [value, setValue] = usestate("");
  
  // 랜더가 일어 날때 마다 새롭게 참조를 생성해서 참조한다.
  const input1Ref = createRef();
  
  // render 될때마다 실행되는것이 아니라 한번 참조하면 그대로!
  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);
  }
}

React Router Hooks

useHistory

  • 함수를 실행하는 것으로 현재 component의 history를 받을 수 있다.

const history = useHistory();
history.push("/");

useParams

const params = useParams();
const id = params.id;

Context API

import React from "react";

const PersonContext = React.createContext();

export default PersonContext;
  • index.js
const persons = [
  {id: 0, name: "Mark", age: 29},
  {id: 1, name: "Hanna", age: 34}
];

ReactDOM.render(
  <Personcontext.Provider value = {persons}>
    <App />
  </Personcontext.Provider>
)
  • Example1.jsx
    :Consumer 사용.
import Personcontext from "../contexts/PersonContext"

export default function Example1() {
  return(
    <Personcontext.Consumer>
      {(persons)=>{
          return (
            <ul>
              { 
                persons.map((person)=>(
                  <li>{person.name}</li>
              ))}
            </ul>
      )}}
    </Personcontext.Consumer>
  );
}
  • Example2.jsx
    : useContext 훅 사용 (가장 많이 사용) -> 함수의 리턴값이 value
import { useContext } from "react";
import Personcontext from "../contexts/PersonContext"

export default function Example1() {
  const persons = useContext(PersonContext);
  return(
  	<ul>
      {persons.map((person)=>(
        <li>{person.name}</li>
      ))}
  	</ul>
  );
}

좋은 웹페이지 즐겨찾기