: 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 사용 )
inputRef = React.createRef();
<input ref={this.inputRef}/>
componentDidMount() {
console.log("componentDidMount", this.inputRef.current.value);
}
( useState, useEffect, useContext )
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>
)
컴포넌트 사이에서 상태와 관련된 로직을 재사용하기 어렵다.
복잡한 컴포넌트들은 이해하기 어렵다
class는 사람과 기계를 혼동시킨다.
this.state는 로직에서 reference를 공유하기 때문에 문제가 발생할 수 있다.
컴포넌트의 라이프사이클에 사용할 수 있다.
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 완벽 가이드)
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;
}
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 가 적용되어 두번 출력됨.
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;
useEffect 와 비슷하게 의존하는 두번째 인자가 변경될때 실행 되도록 한다.
첫번째 인자로 넣은 함수를 변경하는 시점을 의존적으로 사용한다.
즉, 두번째 인자로 넣은 값이 변경될때 첫번째 인자로 넣은 함수도 변경된다.
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)
}
}
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);
}
}
const history = useHistory();
history.push("/");
const params = useParams();
const id = params.id;
import React from "react";
const PersonContext = React.createContext();
export default PersonContext;
const persons = [
{id: 0, name: "Mark", age: 29},
{id: 1, name: "Hanna", age: 34}
];
ReactDOM.render(
<Personcontext.Provider value = {persons}>
<App />
</Personcontext.Provider>
)
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>
);
}
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>
);
}