부트스트랩같은 익스텐션이다..!
그 후
npm install -D tailwindcss postcss autoprefixer
를 통해서 설치해준다.
npx tailwindcss init
를 하면 tailwind.cofig파일이 자동으로 생긴다.
그 후 경로를 넣어준다
그리고 app.css에 @를 해주고..
제대로 적용됐는지 확인해보면 된다.
헬로월드가 정상적으로 뜬다
굿굿
스타일링 해주기
우선 스타일을 다 날려준다.
그 후 div에 className TwaillandCSS를 주면서 적용해나간다
// 함수형
import React, {useState} from "react"; // 리액트 라이브러리에서 컴포넌트 들고오기
import "./App.css";
import Form from "./components/Form";
import List from "./components/List";
export default function App(){ // 컴포넌트를 사용할 수 있게 extends
// state = { //객체로 state 생성
// todoData : [], //배열안에 객체넣기
// value:"",
// };
//state바꿔주기
const [todoData,setTodoData] = useState([]);
const [value,setValue] = useState("");
//this.todoData -> todoData로 바꿔주기
const handleSumbit = (e) =>{
//form아ㄴ에 input전송시 페이지 리로드 막자
e.preventDefault();
//새로운 할 일 데이터
let newTodo = {
id : Date.now(), //유니크한 값
title: value,
completed : false,
}
setTodoData(prev=>[...prev,newTodo]);
setValue("");
// ... : 전개연산자
// 이미 있는거에 새로운거 더해주기
//입력란 안에 있던 글시 지워주기 설명 안하노 ㅋㅋ
}
// 컨테이너를 감싸고
return( // 반환한다
<div className="flex items-center justify-center w-screen h-screen bg-blue-100">
{/* div박스하나를 만든다. 투두블락 */}
<div className="w-full p-6 m-4 bg-white rounded shadow lg:w-3/4 lg:max-w-lg">
{/* 반응형 클래스주기 */}
{/* 그리고 제목박스도 */}
<div className="flex justify-between mb-3">
My To do List
</div>
<List todoData={todoData} setTodoData={setTodoData}/>
<Form handleSumbit={handleSumbit} value={value} setValue={setValue}/>
{/* 밑에서부터 할일 목록을 나열한다 */}
{/* 반복형으로 나열 */}
</div>
</div>
)
}
// 클래스형
// import React, {Component} from "react"; // 리액트 라이브러리에서 컴포넌트 들고오기
// import "./App.css";
// export default class App extends Component{ // 컴포넌트를 사용할 수 있게 extends
// btnStyle = {
// color : "#fff",
// border : "none",
// padding : "5px 9px",
// borderRadius : "50%",
// cursor : "pointer",
// float : "right",
// }
// //style
// getStyle = (completed) =>{
// return{
// padding : "10px",
// borderBottom:"1px #ccc dotted",
// textDecoration : completed ? "line-through" : "none",
// }
// }
// //할일 목록 삭제 함수
// hanndleClick=(id)=>{
// //filter method를 사용해서
// //id가 같은거를 필터링 해버리자
// let newTodoData = this.state.todoData.filter(data=> data.id != id);
// console.log('newTodoData',newTodoData);
// //list의 id가 와서 데이터의 아이디가 아닌것만 트루를 반환해서 살린다
// this.setState({todoData:newTodoData});
// }
// handleChange =(e)=>{
// this.setState({value : e.target.value});
// }
// handleSumbit = (e) =>{
// //form아ㄴ에 input전송시 페이지 리로드 막자
// e.preventDefault();
// //새로운 할 일 데이터
// let newTodo = {
// id : Date.now(), //유니크한 값
// title: this.state.value,
// completed : false,
// }
// //원래 있던 할 일에 새로운 일을 더하자
// this.setState({todoData:[...this.state.todoData,newTodo],value:""});
// // ... : 전개연산자
// // 이미 있는거에 새로운거 더해주기
// //입력란 안에 있던 글시 지워주기 설명 안하노 ㅋㅋ
// }
// state = { //객체로 state 생성
// todoData : [ //배열안에 객체넣기
// {
// id:"1",
// title:"공부하기",
// completed: false
// },
// {
// id:"2",
// title:"청소하기",
// completed: false
// }
// ],
// value:""
// }
// handleCompleteChange = (id) =>{
// let newTodoData = this.state.todoData.map(data=>{
// if(data.id === id){
// data.completed = !data.completed;
// }
// return data;
// })
// this.setState({todoData:newTodoData});
// }
// render(){ // 변환한다
// return( // 반환한다
// // 컨테이너를 감싸고
// <div className="container">
// {/* div박스하나를 만든다. 투두블락 */}
// <div className="todoBlock">
// {/* 그리고 제목박스도 */}
// <div className="title">
// My To do List
// </div>
// {/* 밑에서부터 할일 목록을 나열한다 */}
// {/* 반복형으로 나열 */}
// {this.state.todoData.map(data=>(
// // this는 클래스를 가리키고 클래스 안에 todoData라는 리스트객체를 가지고 와서 그 안에 데이터를 꺼내는데 map함수를 써서 꺼낸다
// // map은 객체별 요소를 data라는 변수로 정해주고 data객체 안에 id,completed,title을 가져온다
// // style같은경우도 겹치는 경우가 많으니 this를 사용해서 클래스 내에 만들어둔 스타일을 가지고 와서 사용한다
// // react에서는 반복되는 값들을 가지고올때 유니크한 값와 같은 key값을 줘야한다
// <div style={this.getStyle(data.completed)} key={data.id}>
// <input type="checkbox" defaultChecked={data.completed} onChange={()=>this.handleCompleteChange(data.id)}></input>
// {data.title}
// <button style={this.btnStyle} onClick={()=>this.hanndleClick(data.id)}>X</button>
// </div>
// ))}
// <form style={{ display : 'flex'}} onSubmit={this.handleSumbit}>
// <input
// type="text"
// name="value"
// style={{flex:'10', padding:'5px'}}
// placeholder="해야할 일 을 입력해주세요"
// value={this.setState.value}
// onChange={this.handleChange}
// />
// <input
// type="submit"
// value="입력"
// className="btn"
// style={{flex:'1'}}
// />
// </form>
// </div>
// </div>
// )
// }
// }
// rfc 엔터하면 함수형 컴포넌트 만들기 가능
import React from 'react'
export default function List({todoData, setTodoData}) {
const handleCompleteChange = (id) =>{
let newTodoData = todoData.map(data=>{
if(data.id === id){
data.completed = !data.completed;
}
return data;
});
setTodoData(newTodoData);
this.setState({todoData:newTodoData});
};
//할일 목록 삭제 함수
const hanndleClick=(id)=>{
//filter method를 사용해서
//id가 같은거를 필터링 해버리자
let newTodoData = todoData.filter(data=> data.id !== id);
console.log('newTodoData',newTodoData);
//list의 id가 와서 데이터의 아이디가 아닌것만 트루를 반환해서 살린다
setTodoData(newTodoData);
};
return (
<div>
{todoData.map(data=>(
// this는 클래스를 가리키고 클래스 안에 todoData라는 리스트객체를 가지고 와서 그 안에 데이터를 꺼내는데 map함수를 써서 꺼낸다
// map은 객체별 요소를 data라는 변수로 정해주고 data객체 안에 id,completed,title을 가져온다
// style같은경우도 겹치는 경우가 많으니 this를 사용해서 클래스 내에 만들어둔 스타일을 가지고 와서 사용한다
// react에서는 반복되는 값들을 가지고올때 유니크한 값와 같은 key값을 줘야한다
<div key={data.id} >
<div className="flex items-center justify-between w-full px-4 py-2 my-2 text-gray-600 bg-gray-100 border rounded ">
<div className="items-center" >
<input type="checkbox" defaultChecked={data.completed} onChange={()=>handleCompleteChange(data.id)}></input>
<span className={data.completed ? "line-through" :undefined}>{data.title}</span>
</div>
<div className="items-center">
<button className="px-4 py-2 float-right" onClick={()=>hanndleClick(data.id)}>X</button>
</div>
</div>
</div>
))}
</div>
)
}
import React from 'react'
export default function Form({handleSumbit,value,setValue}) {
const handleChange =(e)=>{
// this.setState({value : e.target.value});
setValue(e.target.value);
};
return (
<form onSubmit={handleSumbit} className="flex pt-2">
<input
type="text"
name="value"
className="w-full px-3 py-2 mr-4 text-gray-500 rounded shadow"
placeholder="해야할 일 을 입력해주세요"
value={value}
onChange={handleChange}
/>
<input
type="submit"
value="입력"
className="p-2 text-blue-400 border-2 border-blue-300 rounded hover:text-white hover:bg-blue-200"
/>
</form>
)
}
그러면 이렇게 짜라란~
react-build-dnd로 쉽게 구현가능
npm install react-beautiful-dnd --save 로 설치
<DragDropContext /> - Wraps the part of your application you want to have drag and drop enabled for
<Droppable /> - An area that can be dropped into. Cotains <Draggable .?s
<Draggavle /> - What can be dragged around
우선, List.js에 이렇게 코드를 작성해준다
<DragDropContext>
<Droppable>
{todoData.map(data=>(
<Draggable>
<div key={data.id} >
<div className="flex items-center justify-between w-full px-4 py-2 my-2 text-gray-600 bg-gray-100 border rounded ">
<div className="items-center" >
<input type="checkbox" defaultChecked={data.completed} onChange={()=>handleCompleteChange(data.id)}></input>
<span className={data.completed ? "line-through" :undefined}>{data.title}</span>
</div>
<div className="items-center">
<button className="px-4 py-2 float-right" onClick={()=>hanndleClick(data.id)}>X</button>
</div>
</div>
</div>
</Draggable>
))}
</Droppable>
</DragDropContext>
이렇게 넣어주고
return (
<div>
{/* // this는 클래스를 가리키고 클래스 안에 todoData라는 리스트객체를 가지고 와서 그 안에 데이터를 꺼내는데 map함수를 써서 꺼낸다
// map은 객체별 요소를 data라는 변수로 정해주고 data객체 안에 id,completed,title을 가져온다
// style같은경우도 겹치는 경우가 많으니 this를 사용해서 클래스 내에 만들어둔 스타일을 가지고 와서 사용한다
// react에서는 반복되는 값들을 가지고올때 유니크한 값와 같은 key값을 줘야한다 */}
<DragDropContext onDragEnd={handleEnd}>
<Droppable droppableId="todo">
{(provided)=> (
<div {...provided.droppableProps} ref={provided.innerRef}>
{todoData.map((data,index)=>(
<Draggable
key={data.id}
draableId={data.id.toString()}
index={index}
>
{(provided,snapshot)=>(
<div
key={data.id}
{...provided.draggableProps}
ref={provided.innerRef}
{...provided.dragHandleProps}
className={`${snapshot.isDragging ? "bg-gray-400":"bg-gray-100"} flex items-center justify-between w-full px-4 py-2 my-2 text-gray-600 border rounded`}
>
<div className="items-center" >
<input type="checkbox" defaultChecked={data.completed} onChange={()=>handleCompleteChange(data.id)}></input>
<span className={data.completed ? "line-through" :undefined}>{data.title}</span>
</div>
<div className="items-center">
<button className="px-4 py-2 float-right" onClick={()=>hanndleClick(data.id)}>X</button>
</div>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</div>
이렇게 provided를 주면된다.
.그런데 이렇게 해도 드래그하고 위치가 바끼지않으니
onDragEnd에 handleEnd함수를 만들어주자.
const handleEnd = (result) =>{
//result 매개변수에는 source 항목 및 대상 위치와 같은 드래그 이벤트에 대한 정보가 포함됩니다.
console.log(result);
if(!result.destination) return;
}
이렇게 해주고
실제로 todo를 변경해주기 위해서는 splice를 써야한다
splice 메서드는 배열의 기존 요소를 삭제 또는 교체하거나 새 요소를 추가하여 배열의 내용을 변경합니다.
그렇대..추가
삭제
// rfc 엔터하면 함수형 컴포넌트 만들기 가능 import React from 'react' import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
export default function List({todoData, setTodoData}) {
const handleCompleteChange = (id) =>{
let newTodoData = todoData.map(data=>{
if(data.id === id){
data.completed = !data.completed;
}
return data;
});
setTodoData(newTodoData);
this.setState({todoData:newTodoData});
};
//할일 목록 삭제 함수
const hanndleClick=(id)=>{
//filter method를 사용해서
//id가 같은거를 필터링 해버리자
let newTodoData = todoData.filter(data=> data.id !== id);
console.log('newTodoData',newTodoData);
//list의 id가 와서 데이터의 아이디가 아닌것만 트루를 반환해서 살린다
setTodoData(newTodoData);
};
const handleEnd = (result) =>{
//result 매개변수에는 source 항목 및 대상 위치와 같은 드래그 이벤트에 대한 정보가 포함됩니다.
console.log(result);
// 목적지가 없으면 함수 종료
if(!result.destination) return;
// 리액트 불변성 지키기 위한 새로운 tododata생성
const newTodoData = todoData;
// 1. 변경시키는 아이템을 배열에서 지움
// 2. return 값으로 지워진 아이템을 잡음
const [reorderItem] = newTodoData.splice(result.source.index, 1);
// 원하는 자리에 reoderedItem을 invert 한다
newTodoData.splice(result.destination.index, 0, reorderItem);
setTodoData(newTodoData);
};
return (
<div>
{/* // this는 클래스를 가리키고 클래스 안에 todoData라는 리스트객체를 가지고 와서 그 안에 데이터를 꺼내는데 map함수를 써서 꺼낸다
// map은 객체별 요소를 data라는 변수로 정해주고 data객체 안에 id,completed,title을 가져온다
// style같은경우도 겹치는 경우가 많으니 this를 사용해서 클래스 내에 만들어둔 스타일을 가지고 와서 사용한다
// react에서는 반복되는 값들을 가지고올때 유니크한 값와 같은 key값을 줘야한다 */}
<DragDropContext onDragEnd={handleEnd}>
<Droppable droppableId="todo">
{(provided)=> (
<div {...provided.droppableProps} ref={provided.innerRef}>
{todoData.map((data,index)=>(
<Draggable
key={data.id}
draableId={data.id.toString()}
index={index}
>
{(provided,snapshot)=>(
<div
key={data.id}
{...provided.draggableProps}
ref={provided.innerRef}
{...provided.dragHandleProps}
className={`${snapshot.isDragging ? "bg-gray-400":"bg-gray-100"} flex items-center justify-between w-full px-4 py-2 my-2 text-gray-600 border rounded`}
>
<div className="items-center" >
<input type="checkbox" defaultChecked={data.completed} onChange={()=>handleCompleteChange(data.id)}></input>
<span className={data.completed ? "line-through" :undefined}>{data.title}</span>
</div>
<div className="items-center">
<button className="px-4 py-2 float-right" onClick={()=>hanndleClick(data.id)}>X</button>
</div>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</div>
)
}
최종코드이다!