-
나도 만들 수 있다 todolist (외전)웹/react 2020. 5. 18. 04:55반응형
잡담
아... 원래 이거 쓰고 있었는데 한번 날아 가서... 다시 작성합니다. ㅠㅠ
저번까지 했던 파일:
https://github.com/famous0811/Todos
famous0811/Todos
블로그 자료. Contribute to famous0811/Todos development by creating an account on GitHub.
github.com
폴더 분할
폴더구조 이렇게 만들 예정입니다. 참고로 재가 다만든게 아니구여 밑에 있는 출처에서
많이 참고(?) 했습니다.
install
yarn add @reduxjs/toolkit//새로운 redux관리 프로그램 yarn add react-redux//connect사용하기위해서
이 두가지를 추가로 install 해주세요!!
Store and reducer
store폴더에 store.js파일과
reducer폴더를 만든다음
reducer폴더 안에
dayreducer.js
filltereducer.js
todosreducer.js
를 추가해주세요!
그리고 다음과 같이 작성해 주세요 설명은 옆에 달아 놨습니다.!
store.js
import {combineReducers,configureStore} from '@reduxjs/toolkit'; //reducer를 모아주는 함수,store를만들어주는 함수(미들웨어도 추가가) import Todosreducer from './reducer/todosreducer.js'; //todolist를 관리하는 리듀서 import Filterruder from './reducer/filterreducer.js'; //footer를 관리하는 리듀서 import DaysReducer from './reducer/dayreducer.js'; //calender를 관리하는 리듀서 const rootreudcer=combineReducers({//3개의 리듀서를 합침 Todosreducer, Filterruder, DaysReducer }); const store=configureStore({ reducer:rootreudcer//합친 리듀서 연결 }) export default store;//외부 인스톨이 가능하게 해줌
DaysReducer.js
import {createSlice} from '@reduxjs/toolkit';//action 과 reducer를 하나로 연결해줍니다. const date=new Date(); const daySlice = createSlice({ name: 'day',//action type구분용? initialState:{//지금 날짜 세팅(초기값) month:date.getMonth()+1, day:date.getDate() }, reducers:{//리듀서 만들기 SetDays:{ reducer(state, action){ return action.payload;//action.payload로 초기화 }, prepare(month, day){ //setdays라는 리듀서를 호출하면 여기서 payload를 정리한다음에 reducer호출 return { payload: {month,day} } } } } }); export const {SetDays}=daySlice.actions;//외부에서 action함수 호출가능하게 export default daySlice.reducer;//리듀서 호출용
Filterruder.js
import {createSlice} from '@reduxjs/toolkit'; export const filter={//어떤걸 보이게 할지 설정 SHOW_ALL: 'SHOW_ALL',//다 출력 SHOW_COMPLETED: 'SHOW_COMPLETED',//clear한항목 출력 SHOW_ACTIVE: 'SHOW_ACTIVE',//notclear항목 출력 ALL_CLAER:'ALL_CLAER',//전채제거 } const filterSlice =createSlice({ name:"filterview", initialState:filter.SHOW_ALL,//초기값을 all로 설정 reducers:{ setVisibilityFilter(state, action) { return action.payload } } }); export const { setVisibilityFilter } = filterSlice.actions export default filterSlice.reducer
Todosreducer.js
import {createSlice} from '@reduxjs/toolkit'; let nextTodoId=0;//아이디 값 const todoSlices = createSlice({ name: 'todos', initialState:[], reducers:{ addTodo:{//일정추가 reducer(state, action) { const { id, text,month, day} = action.payload state.push({ id, text,month, day, completed: false}) //id,내용,추가한 날짜,완료여부 }, prepare(text,days) { return { payload: { text, id: nextTodoId++,...days} } } }, clearTodo(state,action) {//일정 완료 설정 const todo = state.find(todo => todo.id === action.payload) if (todo) { todo.completed = !todo.completed//완료or완료해제 } }, deleteTodo(state,action) {//일정 제거 return state.filter(item=>item.id!==action.payload); }, Alldelete(state, action){//모든 일정 제거 state.splice(0,state.length); } } }); const {actions,reducer}=todoSlices; export const {addTodo,clearTodo,deleteTodo,Alldelete}=actions; export default reducer;
index.js(수정)
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import {Provider} from 'react-redux'; /*원래 react는 상위 컴퍼넌트가 하위 컴퍼넌트에 props를 전달 해줘야 사용할수 있는데 만약 상위 컴퍼넌트에 props로 스토어를 보내면 하위컴퍼넌트들에게도 직접 전달 해줘야 하니까 불편해서 그런 작업을 생략 시켜줌 갸뀰 */ import store from './store/store.js';//스토어 연결 ReactDOM.render( <React.StrictMode> <Provider store={store}>//연결 <App /> </Provider> </React.StrictMode>, document.getElementById('root') );
containers and components
components폴더를 3개만 남기고 정리해 주세요!
containers폴더를 만든다음에
다음 파일들을 추가해 주세요
※app는 componets안에 넣는 것이 아닙니다.
보기 쉬우라고 이렇게 사진을 편집했습니다.;;
app.jsx
import React from 'react'; import {createGlobalStyle} from 'styled-components'; import ControlList from './components/ControlList'; const GolobalStyle=createGlobalStyle` *{ margin: 0; font-size:14px; } `; function App() {//원래 있던걸 다 controlList에 넣었습니다. return ( <div> <GolobalStyle/> <ControlList></ControlList> </div> ); } export default App;
controlList.jsx
import React from 'react'; import Fotter from '../containers/Fotter'; import Getlist from '../containers/Getlist'; import TodoList from '../containers/TodosList'; import Clalendar from '../containers/Calendar'; //경로도 다 수정되었습니다. function ControlList(props) {//원래 controlList보다 훨씬 깔끔하게 되었습니다. return ( <div> <h1>Todos</h1> <Clalendar></Clalendar> <Getlist></Getlist> <TodoList></TodoList> <Fotter></Fotter> </div> ); } export default ControlList;
Calender.jsx(containers)
import React from 'react'; import Calendarcomponents from '../components/Clalendar'; import {connect} from 'react-redux'; import {SetDays} from '../store/reducer/dayreducer';//위에서 만들었던 reducer호출 function Calendar({SetDays}) {//이거 필수 function Updays(time){ SetDays(time.getMonth()+1,time.getDate());//값을 reducer에 전달 } return ( <div> <Calendarcomponents Updays={Updays}></Calendarcomponents>//calender컴포넌트에 전달 </div> ); } const mapDispatch={SetDays};//이렇게 안하면 호출이 안됨ㅎㅎ export default connect(null,mapDispatch)(Calendar);//리덕스 작업한 함수를 반환
Calender.jsx
import React,{useState} from 'react'; import Calendar from 'react-calendar'; import 'react-calendar/dist/Calendar.css'; function Clalendar({Updays}) {//이거랑 const [Dated,setdate] =useState(new Date()); const onchange = date=>setdate(date); return ( <Calendar className={"calender"} onChange={onchange} onClickDay={Updays}//이거만 수정됨 value={Dated}/> ); } export default Clalendar;
Getlist.jsx
import React,{useState} from 'react'; import { connect } from 'react-redux'; import { addTodo } from '../store/reducer/todosreducer'; const mapDispatch={addTodo} function Getlist({days,addTodo}) { const [todoText, setTodoText] = useState('') const onChange = e => setTodoText(e.target.value) function addlist(e) { e.preventDefault() if (!todoText.trim()) { return } addTodo(todoText,days); setTodoText(''); } return ( <div> <form onSubmit={addlist}> <input value={todoText} onChange={onChange} /> <button type="submit">Add Todo</button> </form> </div> ); } const mapStateToProps =(state) =>{ return{ days:state.DaysReducer//현재 어떤 날자로 설정되어있는지 가져오기 } } export default connect(mapStateToProps,mapDispatch)(Getlist);
TodosList.jsx
import React from 'react'; import {connect} from 'react-redux'; import {createSelector} from '@reduxjs/toolkit'; import {clearTodo,Alldelete,deleteTodo} from '../store/reducer/todosreducer'; import {filter,setVisibilityFilter} from '../store/reducer/filterreducer'; import List from '../components/List'; const selectTodos = state => state.Todosreducer; const selectFilter = state => state.Filterruder; const selectdays=state =>state.DaysReducer; const SetViewTodoset=createSelector(//todos를 전달한 후에 원하는 값만 추출 [selectTodos,selectFilter,selectdays],//현재 store값 가져오기 (todos,nowfilter,days)=>{ switch(nowfilter){//현재 필터 분석 case filter.SHOW_ALL: return todos.filter(t =>days.month===t.month && days.day===t.day) case filter.SHOW_COMPLETED: return todos.filter(t => t.completed && days.month===t.month && days.day===t.day) case filter.SHOW_ACTIVE: return todos.filter(t => !t.completed&& days.month===t.month && days.day===t.day) case filter.ALL_CLAER: //all_clear는 다 지워야 하기 때문에 일단 지워진척한뒤 실제로 지웁니다. return []; default: throw new Error('Unknown filter: '+nowfilter); } } ) function TodosList({todos,nowfilter,clearTodo,Alldelete,deleteTodo,setVisibilityFilter}) { if(nowfilter===filter.ALL_CLAER)//여기서 삭제 합니다 ㅎㅎ { Alldelete([]); setVisibilityFilter(filter.SHOW_ALL); //이렇게 하는 이유는 ALL_CLEAR상태이면 추가해도 계속 삭제 할테니까 그걸 없에기 위해서 했습니다. } return ( <ul id="Lists"> {todos.map(todo=>( <List key={todo.id} {...todo} onclick={()=>clearTodo(todo.id)} test={()=>deleteTodo(todo.id)}/> ))} </ul> ); } const mapStateToProps=(state)=>{ return{ nowfilter:state.Filterruder, todos:SetViewTodoset(state), } } const mapDispatch={clearTodo,Alldelete,deleteTodo,setVisibilityFilter}; export default connect(mapStateToProps,mapDispatch)(TodosList);
list.jsx
import React from 'react'; import styled from 'styled-components'; const Contents =styled.li` text-decoration:${(props)=>props.completed ? 'line-through' : 'none'}; margin-right:10px; `; const Listcontent =styled.div` width:100px; display: flex; justify-content:space-around; flex-direction: row; `; //여기만 보기가 너무 힘들어서 디자인을 살짝 추가 했습니다.ㅎㅎ function List({onclick,test,completed,text,month, day}) { return ( <Listcontent> <Contents onClick={onclick} completed={completed}> {text} </Contents> <div> {month}/{day} </div> <div onClick={test} style={{color: 'red'}}>X</div> </Listcontent> ); } export default List;
Fotter.jsx
import React from 'react'; import {connect} from 'react-redux'; import {filter,setVisibilityFilter} from '../store/reducer/filterreducer'; import {createSelector} from '@reduxjs/toolkit'; import styled from 'styled-components'; const Leight =styled.div` display: ${(props) => props.leight ? "block" : "none"}; `; const Contents=styled.div` border: ${(props) =>props.nowfilter ? '1px solid pink' : "none"}; &:hover{ border:solid 1px pink; } `; const mapDispatch ={setVisibilityFilter}; function Fotter({setVisibilityFilter,nowfilter,leight}) { function Download(){ var lists=document.getElementById("Lists"); //todolist.jsx에 있는 ul테그입니다. var text=""; if(!lists.childElementCount) return;//추가 된것이 없으면 하지 않음 for(var j=0;j<lists.childElementCount;j++){//li태그 수많큼 반복함 var value=lists.childNodes[j]; text+=value.childNodes[0].textContent+" "+value.childNodes[1].textContent+",\n"; /*이렇게 작업 안하고 push하면 되지 않나라고 생각 하셨다면 좋은 생각은 아닙니다.. push로하면 {contents},{...}이런식으로 저장하기 때문에 text로 출력하면 이쉼표도 출력이 됩니다. */ } var element = document.createElement('a'); element.setAttribute('href','data:text/plain;charset=utf-8,'+encodeURIComponent(text)); element.download="Todos.txt"; element.click(); } return ( <div> <Leight leight={leight}>{leight} items left</Leight> <Contents nowfilter={nowfilter===filter.SHOW_ALL} onClick={()=>{setVisibilityFilter(filter.SHOW_ALL)}}>all</Contents> <Contents nowfilter={nowfilter===filter.SHOW_COMPLETED} onClick={()=>{setVisibilityFilter(filter.SHOW_COMPLETED)}}>clear</Contents> <Contents nowfilter={nowfilter===filter.SHOW_ACTIVE} onClick={()=>{setVisibilityFilter(filter.SHOW_ACTIVE)}}>active</Contents> <Contents onClick={()=>{setVisibilityFilter(filter.ALL_CLAER)}}>clear completed</Contents> <Contents onClick={Download}>Download</Contents> </div> ); } const selectTodos = state => state.Todosreducer; const selectFilter = state => state.Filterruder; const selectdays=state =>state.DaysReducer; const selectsize=createSelector( /*이건 길이값을 어떻게 하면 가져올 수 있을까 하다가 끝네 이방법말고는 생각이 않나서 이렇게 만들었습니다 이건 다른 방법있으면 저도좀 알려주세요 ㅎㅎ 필터는 todolist에서 한것과 같습니다 다만 필터를 적용한뒤에 todos에 길이 만 가져옵니다. */ [selectTodos,selectFilter,selectdays], (todos,nowfilter,days)=>{ switch(nowfilter){ case filter.SHOW_ALL: return todos.filter(t =>days.month===t.month && days.day===t.day).length; case filter.SHOW_COMPLETED: return todos.filter(t => t.completed && days.month===t.month && days.day===t.day).length; case filter.SHOW_ACTIVE: return todos.filter(t => !t.completed&& days.month===t.month && days.day===t.day).length; default: return 0; } } ) const commdispatch=function(state,nowstate){ return{ nowfilter:state.Filterruder, leight:selectsize(state) } } export default connect(commdispatch,mapDispatch)(Fotter);
결과:
결과 혹시 똑같이 했는 데 안된다... 면 혹시라도 추가나 변경을 잘못하지 않았다 확인해 주시고 그래도 안된다면....
죄송합니다 ㅠㅠ
아레에 완성본이 있습니다. 이걸보고 해주세요.!! ^^;;;
https://github.com/famous0811/TodosRedux
famous0811/TodosRedux
Contribute to famous0811/TodosRedux development by creating an account on GitHub.
github.com
출쳐:
https://redux-toolkit.js.org/tutorials/intermediate-tutorial#creating-and-using-the-filters-slice
Redux Toolkit
# Intermediate Tutorial: Redux Toolkit in Action
redux-toolkit.js.org
반응형'웹 > react' 카테고리의 다른 글
hooks-1 (useSate, useEffect) (0) 2021.05.13 그그그 팝업 비슷한거 있는데 이름이 뭐지?(모달 페이지) (0) 2021.02.22 나도 만들어 보자 toodolist (3) (0) 2020.05.10 나도 만들어 보자! todolist (2) (0) 2020.05.02 나도 만들어 보자! todolist (기본세팅) (0) 2020.04.28