* useState
* const [name, setName] = useState(‘’);
* useEffect
* componentDidMount + componentDidUpdate
* useEffect은 함수를 컴포넌트가 화면에 맨 처음 렌더링될때만 실행
* 두번째 파라미터로 비어있는 배열을 넣어주면 업데이트될때 수행되지 않는다.
* 특정 값이 변경될 때만 호출하고 싶은 경우는 두번째 파라미터로 넘긴다.
* 특정값을 변경하는 코드를 함께 넣고, 두번째 파라미터에서 특정값이 변경될때 호출하면 무한으로 useEffet가 호출됨
* 업데이트 되기 직전이나, 언마운트 되기 전에 작업을 수행하고 싶으면 return () => { .. cleanup } 함수를 반환한다.
* useReducer
* 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트 해주고 싶을때 사용
* 현재 상태, 업데이트를 위해 필요한 정보를 담는 action값을 전달받아 새로운 상태를 반환하는 함수
* 새로운 상태를 만들 때는 반드시 불변성을 유지?
* 액션 객체는 반드시 type을 지니고 있을 필요가 없다. (문자열, 숫자 상관없음)
* 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다.
* event.target 을 사용하면 action.name을 state로 사용을 할 수 있다.
* reducer에 return { ...state , [action.name]: action.value }}


function reducer(state, action) {
switch (action.type) {
case "INCREMENT":
return {value: state.value + 1}

...
}
}

const [state, dispatch] = useReducer(reducer, {value:0}); // 두번째 파라미터에는 해당 리듀서의 기본값
// state는 현재 가리키고 있는 상태, dispatch는 액션을 발생시키는 함수
onClick => dispatch({type: 'INCREMENT'})}

* useMemo
* 렌더링하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행하고, 원하는 값이 바뀌지 않았다면 이전에 연산했던 결과를 다시 사용
* 값을 재사용(숫자, 문자열, 객체)
* const avg = useMemo(() => getAverage(list), [list]);
* useCallback
* 함수를 재사용
* useMemo와 유사하게 주로 렌더링 성능을 최적화해야 하는 상황에서 사용
* 컴포넌트가 리렌더링 될때마다 함수들이 새로생기기 때문에 useCallback을 이용
* 함수에 useCallBack을 추가해서 컴포넌트가 처음 렌더링될때만 함수 생성 or 특정 값이 변경되었을때만 함수생성(=useEffect와 유사함)
* useRef
* 컴포넌트에서 ref를 쉽게 사용할 수 있도록 해준다.
* 예) 컴포넌트에서 특정 버튼을 눌렀을때 input에 포커스를 가게 하기
* inputEl = useRef(null); <input ref={inputEl} /> inputEl.current.focus()
* 컴포넌트 로컬 변수로 사용 (로컬변수는 렌더링과 상관없이 바뀔수 있는 값) 클래스에서 사용했던 로컬변수를 함수형으로 컴포넌트를 선언하기 위해서
* id = useRef(1), setId = (n) => { id.current = n; } printId = () => {console.log(id.current}
* CustomHooks
* https://github.com/rehooks/awesome-react-hooks
* https://nikgraf.github.io/react-hooks

$ yarn add @elastic/elasticsearch

github가면 예제랑 사용방법이 다 나와있음

$ yarn add @searchkit/refinement-autosuggest
$ yarn add @searchkit/autosuggest

Searchkit/Packages

이걸 보니까 gitbook 작성이 가능하겠다

... 너무 복잡하다 ㅠㅠ 다음에 보게되면 이어서 보자...

ContextProvider를 사용할때 Route에는 어떻게 적용하는게 효율적일지!

<Route path="/app/dashboard">
  <DashboardContext>
    <Dashboard></Dashboard>
  </DashboardContext>
</Route>

반복적으로 위와같이 입력하기 어려우니 ContextRoute를 생성하자


import React from 'react';
import { Route } from 'react-router-dom';

const ContextRoute = ({ provider, component, ...rest }) => {
  const { Provider } = provider;
  const Component = component;

  return (
    <Route {...rest}>
      <Provider>
        <Component />
      </Provider>
    </Route>
  );
};

export default ContextRoute; 

위에 ContextRoute를 정의한 이후에는 다음과같이 작성하면 된다.

<ContextRoute
  path="/app/dashboard"
  provider={DashboardContext}
component={Dashboard}
/>

참고

React Context는 오랫동안 사용이 가능했지만 이번에 React hooks이 나오면서 더 좋아졌다. 이전에는 redux라는 third-party libraries를 사용해서 React Apps의 상태를 관리했었다. 이 개발자가 redux에 대한 내용 을 작성했는데, redux를 사용할 개발자들은 이 해당 포스트를 참고해도 좋을것 같다. (참고로 이 코드를 React context hook 으로 리팩토링을 진행했음

React Redux를 사용할때

나는 react-context-hook을 사용하고 싶으니... 이 코드의 흐름을 이해해보려고 한다. 이전 redux를 사용할때는 component, container, ilb/api, modules를 각각 생성했었는데, 코드 관리가 어려웠다. 하나의 API를 추가하려면 너무 많은 작업이 필요했고, 여러 파일을 변경하면서 수정하는게 보통 쉬운게 아니다. 내가 모듈에 작성했던건지.. lib/api에 작성했던건지 나도 기억이 안나니.. 보통 Redux를 사용하면 Component에 내가 원하는 UI를 표시하고, Container안에 component를 넣고 container에서는 데이터를 lib/api, modules을 통해 가져온다. 역할로 나눠보면 lib/api, modules은 데이터 처리를 해주고, container에서는 데이터 처리의 상태를 확인하다가 변경된 부분이 있다면 데이터를 업데이트를하고, component에서는 container에서 업데이트된 데이터를 화면에 출력을 한다.

위 작업을 하기 위해서는 첫번째로 component를 작성하고, 두번째로 lib/api, module을 작성을 한뒤에 컴포넌트에 데이터가 정상적으로 들어가는지 확인하고, 이후에는 변경되는 데이터를 확인하고 업데이트하는 container를 작성해 최종적으로 component가 아닌 container를 pages에 추가했었다. 이방법이 정답은 아니겠지만 내가 본 책에서는 그랬다. 지금 다른 코드를 보면 이 책도 예전 방식을 따라했던것 같다. 너무 빠르게 변화함..

React Context를 사용하면

React Context를 사용하면 context, container, component를 생성하면된다.

  • Context
    • reducer를 구현해 어떤 액션 타입이 왔을때 어떻게 데이터를 리턴할지에 대해서 명시
    • 추가로 article에 대한 articleContext.js를 생성하고, 그 안에는 ArticleProvider를 정의한다. 이 Provider는 값을 전달하는 역할을 한다.
  • Container
    • container는 context를 이용해 article의 내용을 가져오는 역할을 한다. useContext(ArticleContext를 통해 articles을 가져오고 article을 화면에 출력할 내용을 생성한다. (출력할때 component에 미리 Article 을 만들어놓으면 여기서 사용)
  • Component
    • 위 container에서 작업할 Article을 만든다.
    • ArticleAdd.js를 생성하는데 여기서도 useContext(ArticleContext)을 이용해 dispatch를 가져온다.
      • newAddArticle과 같은 함수를 호출한다.
      • 호출되는 함수에서는 dispatch를 통해 새로운 Article이 추가되었다고 알린다.
  • App.js
    • 최종 앱에는 아래 같은 하위 구성을 정의한다.
      • ArticleProvider (context)
        • AddArticle (component)
        • Articles (container)

참고

React에서 index.js에 Provider로 ReactDOM을 포장을 하는데 그 이유가 이제 전체 App에서 Provider가 제공하는 값들을 사용하기 위해서이다. 보통 LayoutProvider, UserProvider, ThemeProvider를 생성하게 된다.

UserProvider 에서는 사용자의 상태를 관리하고 있어서 App.js에서는 UserProvider를 이용해서 사용자의 로그인 상태를 가져온다. 이렇게 가져온 상태를 통해서 App의 화면을 어떤것을 보여줄지에 대해서 설명한다.

index.js

ReactDOM.render(
  <LayoutProvider>
    <Provider store={store}>
      <ThemeProvider theme={Themes.default}>
        <BrowserRouter>
          <HelmetProvider>
            <App />
          </HelmetProvider>
        </BrowserRouter>
      </ThemeProvider>
    </Provider>
  </LayoutProvider>,
  document.getElementById('root'),
);

src/context/LayoutContext.js

import React from 'react';

var LayoutStateContext = React.createContext();
var LayoutDispatchContext = React.createContext();

function layoutReducer(state, action) {
  switch (action.type) {
    case 'TOGGLE_SIDEBAR':
      return { ...state, isSidebarOpened: !state.isSidebarOpened };
    default: {
      throw new Error(`Unhandled action type: ${action.type}`);
    }
  }
}

function LayoutProvider({ children }) {
  var [state, dispatch] = React.useReducer(layoutReducer, {
    isSidebarOpened: true,
  });
  return (
    <LayoutStateContext.Provider value={state}>
      <LayoutDispatchContext.Provider value={dispatch}>
        {children}
      </LayoutDispatchContext.Provider>
    </LayoutStateContext.Provider>
  );
}

function useLayoutState() {
  var context = React.useContext(LayoutStateContext);
  if (context === undefined) {
    throw new Error('useLayoutState must be used within a LayoutProvider');
  }
  return context;
}

function useLayoutDispatch() {
  var context = React.useContext(LayoutDispatchContext);
  if (context === undefined) {
    throw new Error('useLayoutDispatch must be used within a LayoutProvider');
  }
  return context;
}

export { LayoutProvider, useLayoutState, useLayoutDispatch, toggleSidebar };

// ###########################################################
function toggleSidebar(dispatch) {
  dispatch({
    type: 'TOGGLE_SIDEBAR',
  });
}

App.js

  • App.js에서는 url을 어떤값을 입력했을때 어떤 페이지로 이동할지에 대한 내용을 작성한다.
  • 여기서 사용되는게 react-router-dom이다.
  • 참고로 function App()으로 정의 되어 있었는데, epoxrt default function App()으로 변경하면 아래 export를 따로 해줄필요가 없다. (매번 자동적으로 입력했는데 이게 좋은듯)
  • 이렇게 가져온 isAuthenticated의 값은 global 하게 사용이 가능하다
  • PrivateRoute, PublicRoute를 나누어서 로그인한 사용자, 로그인하지 않은 사용자의 Route를 나눈다.

대시보드를 만들고 싶어서 React를 시작했다. 밑바닥부터 만들려고 보니.. 정말 끝도 없어보였다. 안드로이드 개발을 하면서 느꼈던게 있는 컴포넌트, 라이브러리를 잘사용하는게 빠르게 개발을 할 수 있었다. 웹도 유사하게 훌륭한 개발자들이 많은 컴포넌트와 라이브러리를 제공한다. 제공된 라이브러리와 컴포넌트에 대한 이해를 하면서 개발을 해보는게 좋을것 같다! 항상 갖다쓸때는 왜 이렇게 구현했을까 생각도 하는게 좋은듯! 물론 매번 그렇게 하면... 필요한것만 아주 잘..

일단은 지금 두개의 대시보드를 토대로 새로운 대시보드를 구성해볼까 한다.
react-material-admin의 경우에는 full version은 유료버전이니 참고

+ Recent posts