[20220706_Recoil(리액트전용 State Management Library_1)]

YunTrollpark·2022년 7월 5일
0

React

목록 보기
10/12

Recoil

1. Recoil이란?

: Recoil은 React 전용 State Management Library이다.

2. 왜 Recoil을 사용하는가?

1) Readux


• Redux는 flux pattern의 등장으로 단방향으로 처리 되던 리액트의 단점을 변화시킴
• 외부 저장공간을 사용 → 그러다 보니 강한제약이 생김 → 장점은 많지만 번거로움

2) Recoil

• Recoil을 사용해서 state를 전역으로 관리해주면 코드가 굉장히 깔끔해짐
• React 내부 상태만 이용
• 작은 Atom 단위로 관리
• 순수함수 Selector
• Re-Render 최소화(상태를 변경하면 Atom을 참조하는 component만 re-render함)
• 데이터의 흐름을 따라서
(Recoil의 상태들은 상호 의존성을 가지고 있는데, 데이터 흐름에 따라서 여러 상태들의 연관된 컴포넌트를 유기적으로 관리 가능)
• 읽기전용, 쓰기전용 별도로 제공(설명은 하단에)

3) ReadOnly, Writable 구분해서 사용

3. How to use

→ 이런 구조가 있다는 가정하에!

import {atom} from 'recoil'

export const modalState = atom({ // atom이라는 함수에 key와 기본값 설정해줌
 key: 'modalState',
 default: 'false',
});

// 해당 atom 호출하는 방법(React의 hook과 유사)
// ModalState
import {useRecoilState} from 'recoil';
import {modalState} from '../store/atoms'

function List({data}) { // hooks처럼 호출해서 사용
   const [modlaOpen, setModalOpen] = useRecoilState(modalState);
   const handleRegister = () => {
     setModalOpen(true);
   };
  return (
    <button onClick = {handleRegister}>상품 추가</article>
    {modalOpen && (<RegisterModal />)}
  )
 };

finction RegisterModal() {
  const [modalOpen, setModalOpen] = useRecoilState(modalState);
  const handleClick = () => {
    setModalOpen(false);
  };
  ...
};
// 해당 atom 호출하는 방법
// List State

import {atom} from 'recoil'

export const productState = atom({
  key:'productState',
  default: [],
});

function ProductList() {
  const list = useRecoilValue(productsState); // 읽기 전용
  
  return list && (
    <div className="product-list">
      {list.map((row) => (<Peoduct key={row.idx} data={row />}
      ))}
    </div> 
  );
};
                            
function Product({data}) {
  return (
    data && (
    <article className = "product">
        <img src={data.img} alt={data.name} />
        <dl>
          <dt>[{data.brand}] {data.name}</dt>
          <dt>{data.category}</dt>
          <dt>{data.price}</dt>
        </dl>
    )
  )
};                           
                            

1) Register

function RegisterModal() {
  const [formData, serFormData] = useState(); // (formData는 특히 외부로 공유될 필요가 없어서 굳이 recoil 사용 안함)
  const setList = useSetRecoilState(productsState); // 쓰기 전용
  const setIsOpen = useStateRecoilState(modalState); //쓰기전용
  
  const handleChange = (e) => { // Form 요소의 변경된 내부 state에 담아서
    setFormData({
      ...formData,
      [e.target.name]: e.target,value,
    });
  };
  
  const handleSubmit = async(e) => { // List형 atom에 insert하는 로직
    e.preventDefault();
    setList((prev) => [...prev, formData]);
    setIsOpen(false);
  }; // → 쓰기전용


};

2) Detail

export const productState = atom({ // 상품을 담을 수 있는 atom 정의
  key: 'productState',
  default:{
    idx: 0,
    name:'',
    category:'',
    brand:'',
    price: 0,
    desc:'',
  },
});
function Product({data}) {
  const list = useRecoilValue(productsState);
  const setProduct = useSetRecoilState(productState);

  ...

const handleDetail = (idx) => { // list component에서 각 행을 클릭하면 idx 값을 내려 받아서, list에 있는 data중 idx와 일치하는 값을 찾아서 
  setProduct(list.filter((row) => row.idx === idx)[0]); // product atom에 담음
  setRegisterOpen(true);
};

return(
  data && (
    <article
      className = "product"
      onClick ={()=> handleDetail(data.idx)}>
      ...
    </article>
  )
);
  
}

3) Update

function RegisterModal() {
  const [list, setList]  = useRecoilState(productsState);
  const product = useRecoilValue(productState); // 앞에 담아온 product state를 불러와서 input의 초기값으로 설정
   const resetProduct = useRecoilState(productState);
  
  ...
  
  const handleChange = (e) => { 
    setFormData({
      ...formData,
      [e.target.name]: e.target,value,
    });
  };
  
  const handleSubmit = async(e) => { 
    e.preventDefault();
    
    if(product){ // 데이터가 있는지 여부에 따라서 modal팝업이 등록 팝업인지, 수정팝업인지 분기처리를 해줌
    let newList = list.map((row) =>{
      if(row.idx === product.idx){
      return product;
      } else {
      return row;
      }
    )};
    setList(newList);
    }
    setIsOpen(false);
  }; 
};
// 컴포넌트가 사라질때 해당 값을 초기화 해줘야함, recoil은 전역상태라서 특정값으로 변경되지 않는 이상, 이전 값을 유지함

// 그래서 recoil에서는 reset함수를 추가로 제공
useEffect(() => { // useEffect를 통해서 컴포넌트가 unmount되는 시점에서 값을 초기화
  return () => {
    restProduct();
  };
}, []);

...

<input
  type="text"
  name="name"
  defaultValue={product? .name} // 초기화 하지 않으면 향후 신규 등록시 기존 데이터가 그대로 불러와지는 오류 발생
  onChange={handleChange}
  />

출처: FEConf Korea YouTube

profile
코딩으로 세상에 이야기하는 개발자

0개의 댓글