본문 바로가기

React

[React] Context Api와 redux

리액트 : single page

Single page Application의 단점 : 컴포넌트 간의 state 공유 어려움. 특히, 형제 간의 컴포넌트의 공유

- Lifecycle(생명주기)
  class Detail extends React.Component {
    componentDidMount() {
      // Detail 컴포넌트가 로드 되고나서 실행할 코드
    }
    componentDidUpdate() {
      // Detail 컴포넌트가 업데이트 되고나서 실행할 코드
    }
    componentWillUnmount() {
      // Detail 컴포넌트가 삭제 되기 전에 실행할 코드
    }
  }

  컴포넌트는
  1) 생성이 될 수도 있고(mount)
  2) 재렌더링이 될 수도 있고(update)
  3) 삭제가 될 수도 있다(unmout)

- useEffect 안에 적은 코드는 return 안의 html렌더링 이후에 동작한다
    * useEffect 밖에 적은 코드는 위에서부터 차례대로 실행(렌더링이 나중에 됨)
  # html 렌더링이 빠른 사이트를 원하면 시간이 걸리는 코드는 useEffect() 안에

  # useEffect안에 적는 코드들
  1) 어려운 연산(시간이 걸리는 연산)
  2) 서버에서 데이터를 가져오는 작업
  3) 타이머 등

- useEffect(()=>{실행할 코드}) : mount,update가 될 때마다 실행
- useEffect(()=>{실행할 코드}, []) : mount되었을 때 1번만 실행
- useEffect(()=>{실행할 코드}, [변수]) : 변수가 변할 때만 실행(update시 실행 안됨)
- useEffect(()=>{
    실행할 코드; 
    return()=>{
      실행문보다 먼저 실행할 구문 -> 연속적으로 해야할 것이 있는 경우
    } 
  }, [변수])

App.js

Detail.js로 넘겨준다.

function TabContent({tab, clothes}) { 
    let [fade, setFade] = useState('');
    
    useEffect(()=>{
      setTimeout(()=>{setFade('end')}, 300) 
      return() => {
        setFade('')
      }
    }, [tab])
    
    return ( 
      <div className={`start ${fade}`}>
        { [<div>{clothes.title}</div>, <div>내용1</div>, <div>내용2</div>] [tab] }
      </div>
    )
  }

공동으로 사용할 수 있는 방법

Context Api 문법 

( 잘 사용하지 않음 → 성능 이슈 때문 )
a. 설치 안해도 됨
b. App.js에 import, export 넣어준다.

let [stock, setStock] = useState([10,11,12]);

<Route path='/detail/:id' element={
  <Context1.Provider value={{stock, clothes}}>
    <Detail clothes={clothes} />
  </Context1.Provider>
}/>

       c. 사용하고자 하는 곳에서 임포트 해준다 → Detail.js

 

Redux 외부 라이브러리 사용

 

더보기

설치 : npm i @reduxjs/toolkit@1.8.1 react-redux

⇒ 장바구니

App.js

Cart 컴포넌트 사용 → Cart.js 생성

import { Table } from 'react-bootstrap';

function Cart() {
    return (
        <>
            <Table striped bordered hover>
                <thead>
                    <tr>
                        <th>#</th>
                        <th>상품명</th>
                        <th>수량</th>
                        <th>변경하기</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>1</td>
                        <td>상품</td>
                        <td>1</td>
                        <td>변경</td>
                    </tr>
                </tbody>
            </Table>
        </>
    )
}
export default Cart;

설정 → index.js

import { Provider } from 'react-redux';
import store from './store';

 <Provider store={store}>
  <BrowserRouter>
    <App />
  </BrowserRouter>
</Provider>

src에 store.js 생성 → 컴포넌트가 아니라 저장 공간임
사용할 내용?을 등록해주는

 import { configureStore, createSlice } from "@reduxjs/toolkit";

  let user = createSlice({
    name : 'user', 
    initialState : 'Hong'
 })

 export default configureStore({
    reducer:{
        user : user.reducer
    }
 })

다시 Cart.js

function Cart() {

    let state = useSelector((state) => {return state})
    console.log(state);

    return (

⇒ 위 두 줄을 어디에 넣어도 사용이 가능하다 !

stock에 내용을 추가

 import { configureStore, createSlice } from "@reduxjs/toolkit";

 let user = createSlice({
    name : 'user', 
    initialState : 'Hong'
 })

 let stock = createSlice({
    name : 'stock', 
    initialState : [10, 11, 12]
 })

 let cartItem = createSlice({
    name : 'cartItem', 
    initialState : [{id:0, name:"vest", count:2}, {id:2, name:"jacket", count:1 }]
 })

 export default configureStore({
    reducer:{
        user : user.reducer,
        stock : stock.reducer
    }
 })

Cart.js

let cartItem = useSelector((state) => {return state.cartItem})
  console.log(cartItem);

  return (
      <>
          <Table striped bordered hover>
              <thead>
                  <tr>
                      <th>번호</th>
                      <th>상품명</th>
                      <th>수량</th>
                      <th>변경하기</th>
                  </tr>
              </thead>
              <tbody>
                  <tr>
                      <td>{cartItem[0].id}</td>
                      <td>{cartItem[0].name}</td>
                      <td>{cartItem[0].count}</td>
                      <td>변경</td>
                  </tr>
                  <tr>
                      <td>{cartItem[1].id}</td>
                      <td>{cartItem[1].name}</td>
                      <td>{cartItem[1].count}</td>
                      <td>변경</td>
                  </tr>
              </tbody>
          </Table>
      </>
  )
}
export default Cart;

map을 이용한다.

 {
    cartItem.map((a)=> {
        return (
            <tr>
                <td>{a.id}</td>
                <td>{a.name}</td>
                <td>{a.count}</td>
                <td>변경</td>
            </tr>
        )
    })
}

return 생략 가능

return (
    <>
        <Table striped bordered hover>
            <thead>
                <tr>
                    <th>번호</th>
                    <th>상품명</th>
                    <th>수량</th>
                    <th>변경하기</th>
                </tr>
            </thead>
            <tbody> 
            {
                cartItem.map( a => {
                    return (
                        <tr>
                            <td>{a.id}</td>
                            <td>{a.name}</td>
                            <td>{a.count}</td>
                            <td>변경</td>
                        </tr>
                    )
                })
            }
            </tbody>
        </Table>
    </>
	)
}
export default Cart;

 

Redux의 state를 변경하고 싶으면,

1. state함수 만들기
store.js

  let member = createSlice({
      name : 'member', 
      initialState : 'Kim',
      reducers : {
          changeName(state) {
              return 'jiwon ' + state
          }
      }
   })

2. export하기

export let {changeName} = member.actions

export default configureStore({
    reducer:{
        user : user.reducer,
        stock : stock.reducer,
        cartItem : cartItem.reducer, 
        member : member.reducer
    }

3. 사용하는 곳에서 import, dispatch()로 감싸주어야 한다.

Cart.js

let dispatch = useDispatch()
let member = useSelector(state=>state.member)

  return (
        <>
				{member}의 장바구니
        <Table striped bordered hover>
            <thead>
                <tr>
                    <th>번호</th>
                    <th>상품명</th>
                    <th>수량</th>
                    <th>변경하기</th>
                </tr>
            </thead>
            <tbody> 
            {
                cartItem.map( a => {
                    return (
                        <tr>
                            <td>{a.id}</td>
                            <td>{a.name}</td>
                            <td>{a.count}</td>
                            <td>
                                <button onClick={()=>{
                                    dispatch(changeName())
                                }}> 버튼
                                </button>
                            </td>
                        </tr>
                    )
                })
            }
            </tbody>
        </Table>
        </>
    )
}
export default Cart;

해당 변수에 해당하는 값을 증가시키기 위해 num이라는 변수를 넣어주고,
num.payload 적어준다.

 changeAge(state, num) {
              state.age += num.payload
          }

나이가 5씩 증가한다.
store.js에 내용이 너무 많으면 따로 분리해도 된다.

폴더와 파일을 생성하고 내용을 옮겨준다.

import { configureStore, createSlice } from "@reduxjs/toolkit";

let user2 = createSlice({
    name : 'member', 
    initialState : {name:'Kim', age:20},
    reducers : {
        changeObj(state) {
            state.name = 'Park'
        },
        changeAge(state, num) {
            state.age += num.payload
        }
    }
 })
 export let {changeObj, changeAge} = user2.actions 

 export default user2;

기존 store.js에 분리한 코드들을 사용할 수 있도록 import해준다.

import user2 from './store/userSlice';

Cart.js에서도 경로를 맞춰서 import해준다.

import { changeObj, changeAge } from './../store/userSlice'

장바구니에 상품 추가하기

App.js에서 해당 상품의 이미지를 클릭하면 제품 상세보기 페이지로 가도록 링크 걸어주기

function PListImg(props) {
  let navigate = useNavigate();  // 페이지의 이동을 도와주는 함수
  return (
    <>
      <Col md={4}>
        <Nav.Link onClick={()=>{ navigate(`/detail/${props.i-1}`) }}>
          <img src={`${process.env.PUBLIC_URL}/img/clothes${props.i}.png`}  width="80%"/>
        </Nav.Link>
        <h4>{props.clothes.title}</h4>
        <p>{props.clothes.price}</p>
      </Col>
    </>
  )
}

export default App;

store.js 에서 버튼을 누르면, 장바구니에 추가되도록한다.

addItem(state, action) {
       state.push({id:0, name:"vest", count:1})
    }

임의로 값을 넣어주고, 잘 들어가는지 확인한다.
현재 사용자가 보고 있는 상세페이지에 있는 상품이 들어가도록 해야 한다.

addItem(state, action) {
           state.push(action.payload)
        }

Detail.js

 <Button variant="secondary" onClick={()=>{
      dispatch(addItem({id: findId.id, name:findId.title, count:1}))
    }}
    >주문하기</Button>



'React' 카테고리의 다른 글

[React] TODOLIST  (0) 2024.03.22
[React] 로컬 스토리지  (0) 2024.03.22
[React] axios  (0) 2024.03.22
[React] 리액트 부트스트랩 적용  (0) 2024.03.21
[React] 간단한 맛집 추천 블로그 - 2  (0) 2024.03.21