React 리덕스를 이용해 연락처 페이지 만들기

gu·2022년 12월 16일
0

토이프로젝트

목록 보기
4/6

⭐ UI

💻 Stack

React, Rudux, CSS

💻 기능

  1. 왼쪽에는 연락처 등록하는 폼이 있다.
  2. 오른쪽에는 연락처 리스트와 검색창이 있다.
  3. 리스트에 유저 이름과 전화번호를 추가할 수 있다.
  4. 리스트에 아이템이 몇개있는지 보인다.
  5. 사용자가 유저를 이름검색으로 연락처를 찾을수 있다.
  6. 검색창에 빈값을 넣으면 다시 전체 연락처가 뜬다.

💻 코드정리

⚡index.js

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

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <App />
  </Provider>
);

리덕스를 이용하여 프로젝트를 진행할것이기에 App.js를 Provider로 감싸고 store를 props로 보냈다. (리덕스 기본 셋팅)

⚡Store.js

import { createStore } from "redux";
import reducer from "./reducer/reducer";

let store = createStore(reducer); 

export default store;

액션을 받을 리듀서를 store에 저장한다.

⚡App.js

function App() {
  return (
    <div className='main-page'>
      <h1 className='title'>Phone Book</h1>
      <Container> // 리액트부트스트랩 이용
       <Row>
       <Col>
          <ContactForm></ContactForm> // 연락처등록컴포넌트
        </Col>
        <Col>
          <ContactList></ContactList> //연락처리스트 컴포넌트
        </Col>
       </Row>
      </Container>
    </div>
  );
}

반응형을 위해 리액트 부트스트랩 컴포넌트에 연락처등록 박스랑 연락처 리스트를 넣었다. 등록박스에 연락처를 추가할때마다 리스트에도 동시에 추가가되어야한다.

⚡ContactForm.js

import React from 'react'
import { Form ,Button } from 'react-bootstrap'
import { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'

const ContactForm = () => {
    //onChange : 타입할때마다 타입한 값을 저장

    const [name,setName]=useState('')
    const [phoneNumber, setPhoneNumber]=useState(0)
    const dispatch = useDispatch();
    

    const addContact=(event)=>{
        event.preventDefault(); //새로고침을 막아줌
        dispatch({type:"ADD_CONTACT", payload:{name,phoneNumber}}) //name:name, phoneNumber:phoneNumber줄임 자스 es6최신문법
    }
   
  return (
    <div>
        <Form onSubmit={addContact}> 
      <Form.Group className="mb-3" controlId="formBasicEmail">
        <Form.Label>Name</Form.Label>
        <Form.Control type="text" placeholder="이름을 입력하세요" onChange={(event)=>setName(event.target.value)}/> 
      </Form.Group>

      <Form.Group className="mb-3" controlId="formBasicPassword">
        <Form.Label>Phone Number</Form.Label>
        <Form.Control type="number" placeholder="전화번호를 입력하세요" onChange={event=>setPhoneNumber(event.target.value)} />
      </Form.Group>
      <Button variant="primary" type="submit">
        추가
      </Button>
    </Form>
    </div>
  )
}

폼의 기본구조는 리액트 부트스트랩을 이용하였다. type이 'submit'일 경우 추가버튼을 누를때마다 새로고침이 된다. 그것을 막아주기위해 해당 폼에 onSubmit이벤트를 넣어 addContact함수를 실행시킨다. addContact함수에는 event.preventDefault()함수를 넣어 새로고침하는 것을 막아줬다. 그리고 액션타입과 payload(폼에서 받아온 이름과 번호를 넘겨야하므로 name과 phoneNumber을 넣어준다.)을 dispatch를 통해 리듀서에 보낸다.

❗이름과 번호 정보는 폼의 인풋에 onChange이벤트를 실행시켜서 useState을 통해 저장해두자!
❗리액트에서 인풋값을 받아올때는 e.target.value이다. (자바스크립트에서는 target을 뺀다.)

⚡ContactList.js

import React from 'react'
import ContactItem from './ContactItem'
import SearchBox from './SearchBox'
import { useState, useEffect } from 'react';
import {useSelector} from "react-redux";

const ContactList = () => {
    const {contact,keyword} = useSelector(state=>state) //리듀스에서 가져온 객체

    const [filterList,setFilterList] = useState([]) // 연락처저장을 위함


    useEffect(()=>{
        if(keyword !==""){ // 검색어가 빈칸이 아닐때
            let list = contact.filter((item) => item.name.includes(keyword)); 
            setFilterList(list); // 검색어가 포함된 배열 아이템을 setFilterList에 담는다.
        }else{ // 빈칸일때는
            setFilterList(contact); // 배열 아이템 모두 담는다.
        }
    },[keyword]) // 렌더링시 키워드를 중시
  return (
    <div>
        <SearchBox></SearchBox> //연락처 검색박스
        <span>num:{filterList.length}</span> // 연락처 아이템개수
        {filterList.map((item)=>(<ContactItem item={item}></ContactItem>))}
        // filterList에 저장된 아이템을 props로 ContactItem컴포넌트에 전달
        
    </div>
  )
}

⚡SearchBox.js

import React, { useState } from 'react'
import {Row,Col,Form,Button} from "react-bootstrap"
import { useDispatch, useSelector } from 'react-redux'


const SearchBox = () => {
    const [keyword,setKeyword] = useState('') // 검색어 저장state
    let dispatch = useDispatch();
    let {contact} =useSelector((state)=>state) //리듀서에서 가져옴

    const searchName=(event)=>{
        event.preventDefault(); //'submit'타입의 새로고침을 막기위함
        dispatch({type:"SEARCH_NAME",payload:{keyword}})
        // 검색어를 dispatch에 전달
    }

  return (
       <div className='search-box'>
         <Form onSubmit={searchName}> // 제출과 동시에 searchName실행
         <Row>
            <Col lg={10}>
                <Form.Control type="text" placeholder="이름을 입력하세요" onChange={(event)=>setKeyword(event.target.value)}/>
            </Col>
            <Col lg={2}>
                <Button type="submit">Search</Button>
            </Col>
        </Row>
         </Form>
       </div>
  )
}

검색박스 폼에서도 검색키워드 값을 keyword에 전달해주기 위해 event.target.value을 사용했다.

⚡ContactItem.js

import React from 'react'
import {Col,Row} from "react-bootstrap"

const ContactItem = ({item}) => {
  return (
    <div className='contact-box'>
        <Row>
            <Col lg={2} className='image-icon'>
                <img width={80} src='https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSG-YUd4SDH1tKFZUo5wd8M7X5iIpcVyrDwsQ&usqp=CAU'></img>
            </Col>
            <Col lg={10} className='item-box'>
                <div>{item.name}</div>
                <div>{item.phoneNumber}</div>
            </Col>
        </Row>
    </div>
  )
}

props로 전달된 아이템(연락처리스트)을 받아 이름과 번호를 렌더링시킨다.

⚡reducer.js

let initialState={ // 초기state는 필수!
    contact:[], //받아온연락처를 여기에 추가
    keyword:"", //검색 키워드 저장
};

function reducer(state=initialState, action){
    const {type,payload}=action; 
    //type과 payload는 action으로부터 뽑아냄 (action.type앞에 액션붙일 필요가없음)

  switch(type){
    case "ADD_CONTACT":
        // return{...state,contact:[...state.contact,{name:payload.name,phoneNumber:payload.phoneNumber,},],}; 
        // break;
        state.contact.push({
            name: payload.name,
            phoneNumber: payload.phoneNumber,
          });  break;
    case "SEARCH_NAME":
        state.keyword = payload.keyword;
        break;
        // return{...state,keyword : payload.keyword};
        // default: return{...state}
  };
  return { ...state };
}

dispatch를 통해 전달받은 타입에 대한 액션을 만들어줘야한다. switch문을 이용했다.(if문도가능)
ADD_CONTACT타입일때 name에 payload.name를, phoneNumber에 payload.phoneNumber을 넣어 state.contact(initialState안에 배열)준다.

SEARCH_NAME타입일때 state.keyword에 payload.keyword값을 보낸다.

💥 도메인 & 깃주소

연락처페이지도메인
깃허브주소

0개의 댓글