개발자로 전향중

리액트 4주차 mydictionary 2022-01-28 기능구현 완료! 본문

React

리액트 4주차 mydictionary 2022-01-28 기능구현 완료!

hovinee 2022. 1. 29. 09:00

기능구현 완료!

App.js

 

import { Route, Routes } from "react-router-dom";
import React from "react";
import Page from "./Page";
import AddPage from "./addPage";
import DetailPage from "./detailPage";


function App() {
  
    
  return (
    <div className="App">
        <Routes>
          <Route path="/" element={<Page />} />
          <Route path="/addpage" element={<AddPage />} />
          <Route path="/detailpage:index" element={<DetailPage />} />
        </Routes>
    </div>
  );
}


export default App;

 

Page.js

 

import React from "react";
import styled from "styled-components";
import { useNavigate } from "react-router-dom";
import { useSelector } from "react-redux"
import { IoIosAddCircle } from "react-icons/io";
import { useDispatch } from "react-redux";
import { updateDic, deleteDic } from "./redux/modules/dic";

const Page = () => {
  
    const dispatch = useDispatch();
    let navigate = useNavigate();
    const inputs = useSelector((state) => state.dic.inputs);
   

    return (
        <>
            <Title>My Dictionary</Title>
            <IoIosAddCircle size={50} color="purple"
                onClick={() => { navigate("/addpage"); }} />
            <Line />
            <Container>
       
                {inputs.map((input, index) => {
                    return (
                        <Input key={index} completed={input.completed}>
                            <h4 style={{
                                fontSize: "24px"
                            }}>{input.word}</h4>
                            <span>[{input.mean}]</span>
                            <p>{input.explain}</p>
                            <p>{input.example}</p>
                            <p>{input.same}</p>
                            <button onClick={() => {
                                dispatch(updateDic(index));
                            }}>완료</button>
                            <button onClick={() => {
                                 navigate("/detailpage"+index);
                            }}>수정</button>
                            <button onClick={() => {
                                dispatch(deleteDic(index));
                            }}>삭제</button>
                        </Input>

                    );
                })}
         
            </Container>
        </>

    );
};



const Title = styled.h1`
    color: slateblue;
    text-align: center;
    `;

const Line = styled.hr`
    margin: 16px 10px;
    border: 2px dotted #ddd;
    `;

const Container = styled.div`
    max-width: 80vw;
    min-height: 100vh;
    background-color: #FFFAFA;
    padding: 30px;
    margin: 100px auto;
    border-radius: 5px;
    border: 1px solid #D8BFD8;
    `;


// props는 Input의 props를 넘겨주는 것! 자바스크립트를 쓰려면 ${}를 넣어줘야함
const Input = styled.div`
  background-color: ${(inputs) => (inputs.completed ? "#673ab7" : "#E6E6FA")};
  border-radius: 10px;
  border: 1px solid #D8BFD8;
  width: 20vw;
  float: left;
  margin: 10px 20px
`;


export default Page;

 

 

addPage.js

 

// 리액트 패키지를 불러옵니다.
import React from 'react';
import styled from "styled-components";
import { useDispatch } from "react-redux";
import { createDic } from "./redux/modules/dic"
import { useNavigate } from "react-router-dom";
import Button from '@mui/material/Button';



const AddPage = () => {
  let navigate = useNavigate();

  const word = React.useRef(null);
  const mean = React.useRef(null);
  const explain = React.useRef(null);
  const example = React.useRef(null);
  const same = React.useRef(null);
  const dispatch = useDispatch();


  const addDictList = () => {
    if (word.current.value === "") {
      window.location = "/addpage"
      alert("단어를 입력해주세요")
    } else if (explain.current.value === "") {
      window.location = "/addpage"
      alert("설명을 입력해주세요")
    } else if (example.current.value === "") {
      window.location = "/addpage"
      alert("예시를 입력해주세요")
    } else {
      dispatch(createDic({
        word: word.current.value,
        explain: explain.current.value, 
        example: example.current.value,
        mean : mean.current.value,
        same : same.current.value,
        completed: false,
      }
      ));
    }
  };

  return (
    <>
      <Container>
        <Title>내 사전</Title>
        <Line />
        <ItemStyle>
            <Input>
          <input name="name" placeholder="단어" ref={word} />
        </Input>
        <Input>
          <input name="mean" placeholder="뜻" ref={mean} />
        </Input>
        <Input>
          <input name="explain" placeholder="설명" ref={explain} />
        </Input>
        <Input>
          <input name="example" placeholder="예시문" ref={example} />
        </Input>
        <Input>
          <input name="same" placeholder="비슷한단어" ref={same} />
        </Input>
        <Button
          style={style.button}
          variant='outlined'
          color='secondary'
          onClick={() => {
            addDictList();
            // 온클릭 중복하려면 뒤에() 붙여야한다
            navigate("/");
          }}>추가하기</Button>
        </ItemStyle>
      

      </Container>
      

    </>

  );
};

export default AddPage;

const style = {
  button: {
    width: "90%",
    display: "block",
    margin: "30px auto 0px auto",
    padding: "5px"
  },
  text: {
    textAlign: "center"
  }
  
}

const Container = styled.div`
    max-width: 350px;
    min-height: 60vh;
    background-color: #FFFAFA;
    padding: 16px;
    margin: 100px auto;
    border-radius: 5px;
    border: 1px solid #ddd;
    `;

const Title = styled.h1`
  color: slateblue;
  text-align: center;
  `;

const Line = styled.hr`
    margin: 16px 10px;
    border: 2px dotted #ddd;
    `;


const ItemStyle = styled.div`
    padding: 5px;
    margin: 50px 8px 8px 8px;
    background-color: #E6E6FA;
    line-height: 20px
    `;


const Input = styled.div`
  max-width: "100%",
  min-height: 20vh;
  background-color: #E6E6FA;
  padding: 10px;
  margin: 10px 10px;
  
 
  display: flex;
  & > * {
    padding : 10px;
  }

  & input{
    border: 1px solid #888;
    width: 100%;
  }

  & input:focus {
    border: 1px solid #a673ff;
    outline: none;
  }

`;

 

 

detailPage.js

 

// 리액트 패키지를 불러옵니다.
import React from 'react';
import styled from "styled-components";
import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import Button from '@mui/material/Button';
import { amendDic } from "./redux/modules/dic"
import { useParams } from "react-router-dom";


const DetailPage = (props) => {
  const {index} = useParams();
  
  const dic_index = {index}.index
  
 
  
  let navigate = useNavigate();

  const word = React.useRef(null);
  const mean = React.useRef(null);
  const explain = React.useRef(null);
  const example = React.useRef(null);
  const same = React.useRef(null);
  const dispatch = useDispatch();


  const amendDictList = () => {
    if (word.current.value === "") {
      window.location = "/addpage"
      alert("단어를 입력해주세요")
    } else if (explain.current.value === "") {
      window.location = "/addpage"
      alert("설명을 입력해주세요")
    } else if (example.current.value === "") {
      window.location = "/addpage"
      alert("예시를 입력해주세요")
    } else {
      
        dispatch(amendDic({
        word: word.current.value,
        explain: explain.current.value, 
        example: example.current.value,
        mean : mean.current.value,
        same : same.current.value,
        completed: false,
        index : dic_index
      }
      ));
    }
  };

  return (
    <>
      <Container>
        <Title>내 사전</Title>
        <Line />
        <ItemStyle>
            <Input>
          <input name="name" placeholder="단어" ref={word} />
        </Input>
        <Input>
          <input name="mean" placeholder="뜻" ref={mean} />
        </Input>
        <Input>
          <input name="explain" placeholder="설명" ref={explain} />
        </Input>
        <Input>
          <input name="example" placeholder="예시문" ref={example} />
        </Input>
        <Input>
          <input name="same" placeholder="비슷한단어" ref={same} />
        </Input>
        <Button
          style={style.button}
          variant='outlined'
          color='secondary'
          onClick={() => {
            amendDictList();
            // 온클릭 중복하려면 뒤에() 붙여야한다
            navigate("/");
          }}>수정하기</Button>
        </ItemStyle>
      

      </Container>
      

    </>

  );
};

export default DetailPage;

const style = {
  button: {
    width: "90%",
    display: "block",
    margin: "30px auto 0px auto",
    padding: "5px"
  },
  text: {
    textAlign: "center"
  }
  
}

const Container = styled.div`
    max-width: 350px;
    min-height: 60vh;
    background-color: #FFFAFA;
    padding: 16px;
    margin: 100px auto;
    border-radius: 5px;
    border: 1px solid #ddd;
    `;

const Title = styled.h1`
  color: slateblue;
  text-align: center;
  `;

const Line = styled.hr`
    margin: 16px 10px;
    border: 2px dotted #ddd;
    `;


const ItemStyle = styled.div`
    padding: 5px;
    margin: 50px 8px 8px 8px;
    background-color: #E6E6FA;
    line-height: 20px
    `;


const Input = styled.div`
  max-width: "100%",
  min-height: 20vh;
  background-color: #E6E6FA;
  padding: 10px;
  margin: 10px 10px;
  
 
  display: flex;
  & > * {
    padding : 10px;
  }

  & input{
    border: 1px solid #888;
    width: 100%;
  }

  & input:focus {
    border: 1px solid #a673ff;
    outline: none;
  }

`;

 

 

dic.js

 

const LOAD   = 'dic/LOAD';
const CREATE = "dic/CREATE";
const DELETE = "dic/DELETE";
const UPDATE = "dic/UPDATE";
const AMEND = "dic/AMEND";
// 초기 상태값을 만들어줍니다.
const initialState = {
  inputs: [],
};

// 액션 생성 함수예요.
// 액션을 만들어줄 함수죠!
export function loadDic(dic) {
  return { type: LOAD, dic };
}

export function createDic(dic) {
  return { type: CREATE, dic: dic };
}

export function updateDic(index) {
  return { type: UPDATE, index };
}

export function amendDic(amendDic) {
  return { type: AMEND, amendDic: amendDic};
}


export function deleteDic(index) {
  return { type: DELETE, index };
}

// 리듀서예요.
// 실질적으로 store에 들어가 있는 데이터를 변경하는 곳이죠!
export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
    case "dic/LOAD":
      return state;

    case "dic/CREATE": {
      const new_dic_list = [...state.inputs, action.dic];
      return { inputs: new_dic_list };
      
    }

    case "dic/AMEND": {
      const new_dic_list = state.inputs.map((dic, idx) => {
        if(parseInt(action.amendDic.index) === idx) {
         
          return { ...dic, word:action.amendDic.word,
                           mean:action.amendDic.mean, 
                           explain:action.amendDic.explain, 
                           example:action.amendDic.example,
                           same:action.amendDic.same,
                           completed:action.amendDic.completed};
        } else {
          return dic
        }
      })
     
      return { inputs: new_dic_list }
    }

    case "dic/UPDATE": {
      const new_dic_list = state.inputs.map((l, idx) => {
        if (parseInt(action.index) === idx) {
          if(state.inputs[idx].completed === false) {
            return { ...l, completed: true };
          } else {
            return { ...l, completed: false };
          }
        } else {
          return l
        }
      })
      return { inputs: new_dic_list }
    }

    case "dic/DELETE": {
      const new_dic_list = state.inputs.filter((l, idx) => {
        console.log(
          parseInt(action.index) !== idx,
          parseInt(action.index),
          idx
        )
        return parseInt(action.index) !== idx;
        // [1,2,3]에서 2를 빼고 싶으면  2만 빼는게 아니라 [1,3]의 값으로 재반환!
        // return은 true와 false로 줄 수 있는데 ture면 현재요소가 들어가고 false면 
        // 새배열에 이 요소가 들어가지 않는다. true면 값이 들어가는데 false라서 안들어간데!
      });

      return { inputs: new_dic_list };
    }
    default: 
      return state;
  }
}

 

 

configStore.js

 

//configStore.js
import { createStore, combineReducers } from "redux";
import dic from "./modules/dic";

// root 리듀서를 만들어줍니다.
// 나중에 리듀서를 여러개 만들게 되면 여기에 하나씩 추가해주는 거예요!
const rootReducer = combineReducers({ dic });

// 스토어를 만듭니다.
const store = createStore(rootReducer);

export default store;

 

index.js

 

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from "react-router-dom";

// 우리의 버킷리스트에 리덕스를 주입해줄 프로바이더를 불러옵니다!
import { Provider } from "react-redux";
// 연결할 스토어도 가지고 와요.
import store from "./redux/configStore";

ReactDOM.render(
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>,
  document.getElementById("root")
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();