React

React - App 컴포넌트에서 useReducer사용

Starters 2020. 9. 11. 15:20

 

 

React - useReducer

이전에는 useState로 상태 업데이트 해줬음 useReducer로도 할 수 있음 useState는 setValue(5); 와 설정하고 싶은 다음 상태를 직접 지정해줘서 업데이트함 useReducer는 action이라는 객체를 기반으로 상태를 �

startersdev.tistory.com

 

1. App 컴포넌트에서 사용할 초기 상태를 컴포넌트 밖에 선언해 줌

const initialState = {
  inputs: {
    username: '',
    email: '',
  },
  users: [
    {
      id: 1,
      username: 'starters',
      email: 'public.starters@gmail.com',
      active: true,
    },
    {
      id: 2,
      username: 'tester',
      email: 'tester@gmail.com',
      active: false,
    },
    {
      id: 3,
      username: 'jajajojo',
      email: 'jajajojo@gmail.com',
      active: false,
    }
  ]
}

 

2. App컴포넌트에서 사용한 모든 로직 지우기, 내부 props도 지우기

function App() {

  return (
    <>
      <CreateUser
      />
      <UserList users={[]}/>
      <div>활성 사용자 수: 0</div>
    </>
  );
}

- users에 임시로 빈 배열 넣고, count는 0으로

 

3. useState 대신에 useReducer 불러오기

import React, { useRef, useReducer, useMemo, useCallback } from 'react';

 

4. reducer 틀 잡기

function reducer(state, action){
  return state;
}

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      <CreateUser
      />
      <UserList users={[]}/>
      <div>활성 사용자 수: 0</div>
    </>
  );
}

 

 

5. state 안에 있는 users와 inputs 비구조 할당으로 추출 후, 컴포넌트를 통해 props로 전달

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { users } = state;
  const { username, email } = state.inputs;
  return (
    <>
      <CreateUser
        username={username}
        email={email}
      />
      <UserList users={users}/>
      <div>활성 사용자 수: 0</div>
    </>
  );
}

 

6. onChange함수 구현하고 사용

const onChange = useCallback(e => {
    const { name, value } = e.target;
    dispatch({
      type: 'CHANGE_INPUT',
      name,
      value
    });
  }, []);

  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
      />
      ...

 

 

 

7. onCreate()

const onCreate = useCallback(() => {
      dispatch({
        type: 'CREATE_USER',
        user: {
          id: 1,
          username,
          email,
        }
      });
    }, [username, email]);

    return (
      <>
        <CreateUser

- id 는 임의로 1, 나중에 useRef로 관리해주어야 함

 

1) nextId 값 관리

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const nextId = useRef(4);
  const { users } = state;

- App함수 상단에 선언, 초기값은 4 기존에 3개 들어있기 때문

 

2) create 함수에 넣어줌

const onCreate = useCallback(() => {
      dispatch({
        type: 'CREATE_USER',
        user: {
          id: nextId.current,
          username,
          email,
        }
      });
      nextId.current += 1;
    }, [username, email]);

3) reducer 함수에도 추가

function reducer(state, action){
  switch (action.type) {
    case 'CHANGE_INPUT':
      return {
        ...state,
        inputs: {
          ...state.inputs,
          [action.name]: action.value
        }
      };
    case 'CREATE_USER':
      return {
        inputs: initialState.inputs,
        users: state.users.concat(action.user)
      }
    default:
      throw new Error('Unhandled action');

  }
}

4) props도 지정

<CreateUser
  username={username}
  email={email}
  onChange={onChange}
  onCreate={onCreate}
/>

-> 이러고 나면 새 글 생성할 수 있음

 

8. onToggle / onRemove

- reducer 안에 먼저 추가

case 'TOGGLE_USER':
  return {
    ...state,
    users: state.users.map(user =>
      user.id === action.id
        ? { ...user, active: !user.active }
        : user
  )
};
case 'REMOVE_USER':
	return {
      ...state,
      users: state.users.filter(user => user.id !== action.id)
};

- 함수 생성, props 추가

const onToggle = useCallback(id => {
      dispatch({
        type: 'TOGGLE_USER',
        id
      });
    }, []);

    const onRemove = useCallback(id => {
      dispatch({
        type: 'REMOVE_USER',
        id
      });
    }, []);

    return (
      <>
        <CreateUser
          username={username}
          email={email}
          onChange={onChange}
          onCreate={onCreate}
        />
      <UserList users={users}
        onToggle={onToggle}
        onRemove={onRemove}/>
      <div>활성 사용자 수: 0</div>
    </>
  );

 

9. 활성 사용자 수

const count = useMemo(() => countActiveUsers(users), [users]);

    return (
      <>
        <CreateUser
          username={username}
          email={email}
          onChange={onChange}
          onCreate={onCreate}
        />
      <UserList users={users}
        onToggle={onToggle}
        onRemove={onRemove}/>
      <div>활성 사용자 수: {count}</div>
    </>

- 이전과 동일, useMemo 사용하고 props 추가

 

 

※ 언제 useReducer 사용하고 useState 사용할 지

- 확실하게 정해진 것은 없음

- 값이 한두개이거나 boolean값이면 useState가 편할 것

- 컴포넌트에서 관리하는 값이 여러개이거나 구조가 복잡하면 useReducer가 나을 수 있음

- setUsers, setInputs 처럼 useState자주 사용되면 useReducer 사용을 고려해보는 것도 좋음

 

 

 

이전 코드

import React, { useRef, useState, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';

function countActiveUsers(users){
  console.log('활성 사용자 수를 세는 중...');
  return users.filter(user => user.active).length;
}

function App() {
  const [inputs, setInputs] = useState({
    username: '',
    email: ''
  });
  const {username, email} = inputs;
  const onChange = useCallback(e => {
    const {name, value} = e.target;
    setInputs({
      ...inputs,
      [name]: value
    });
  }, [inputs]);

  const [users, setUsers] = useState([
    {
      id: 1,
      username: 'starters',
      email: 'public.starters@gmail.com',
      active: true,
    },
    {
      id: 2,
      username: 'tester',
      email: 'tester@gmail.com',
      active: false,
    },
    {
      id: 3,
      username: 'jajajojo',
      email: 'jajajojo@gmail.com',
      active: false,
    }
  ]);

  const nextId = useRef(4);

  const onCreate = useCallback(() => {
    const user = {
      id: nextId.current,
      username,
      email,
    };
    setUsers(users => users.concat(user));
    setInputs({
      username: '',
      email: ''
    });

    console.log(nextId.current);

    nextId.current += 1;
  }, [username, email]);

  const onRemove = useCallback(id => {
    setUsers(users => users.filter(user => user.id !== id))
  }, []);

  const onToggle = useCallback(id => {
    setUsers(users => users.map(
      user => user.id === id
        ? { ...user, active: !user.active }
        : user
    ));
  }, []);

  const count = useMemo(() => countActiveUsers(users), [users]);

  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
      <div>활성 사용자 수: {count}</div>
    </>
  );
}

export default App;