상태를 ListPage가 들고있고
글쓰기 페이지에서는 리스트 페이지에 접근할 수 있는 방법이 없다
리스트페이지와 글쓰기 페이지는 완전 관련이 없다
파라미터로 날리는 것 외에는 없다
실제로 이렇게 구현을 많이하는데
리스트페이지는 서버한테 api요청해서 다운을 받을 것이고
글쓰기페이지는 데이터를 리스트페이지로 보내지않고 DB에 인서트하는 것이다
그러면 리스트는 DB에서 받아서 뿌리는 것이지 글쓰기페이지에서 받아오는 것이 아니다
현재 내가 하려는 것은 글쓰기에서 작성을해서 리스트에 상태값을 넣으려는 것이다
그것은 불가능하다
하나 방법은 있는데 그 방법은 App.js에서 상태를 들고 있으면 된다
굳이 이렇게 할 필요는 없지만 굳이 해야한다면 상태를 부모 페이지가 들고있고
그 안에 자식페이지가 전부 들어가 있으면 되는데
예를들어 부모페이지에서 const [posts, setPosts] = useState([원하는데이터])
이런식으로 상태값을 만들고
원하는 경로안에 ()=> js파일이름 이런식으로 전달하면 가능하지만
굳이 이렇게 쓸 필요는 없다
글쓰기와 리스트 페이지는 따로 되어있으므로 글쓰기에는 setPosts를 날리고
리스트페이지에는 posts를 같이 날려주면되는데 거의 쓸 일 없으니 그렇구나 하고 넘어가자
** 여기서잠깐 ! **
지금보고있는 자료에서는 리액트 v5를 기준으로 공부를 쭉 이어오고있는데
확인해보니 v6부터 바뀐게 상당히 많다
exact가 일단 사라졌고 /*을 넣어서 해당하지 않는 url을 404notFound이런식으로
문구를 넣어서 처리할 수 있고
Route를 많이쓸 경우 Routes로 묶어서 사용해줘야하며
Switch에서 Routes로 변경되었다
component 가 element로 변경되었고
path 를 기존의 path="/Login/:id" 에서 path=":id" 로, 상대경로로 지정할 수 있고
component 방식도 변경되어서
(component={COM} 및 render={() => <h1>HelloWorld<h1/>} 삭제)
useHistory대신 useNavigate를 사용하고
그 밖에 변경사항이 많아서 나같이 막 시작한 입장에서는
자료와 강의가 버전업이 안되서 겪는 어려움이 많다
공식문서를 자주 읽은 습관을 만드는 것이 중요할 것 같다
내가 할 실습은 라우터를 없애고 하나의 페이지에서 실행해볼 생각이다
App.js에 있던 리스트페이지 경로와 글쓰기 경로를 지우고
<ListPage/>
이런식으로 하나만 넣어주고
글쓰기 페이지에서 form을 전부 복사해서
리스트페이지 hr위에 붙여넣어준다
그리고 글쓰기 페이지에서 HandleWrite라는 것도 들고오도록 하자
자 이제 준비가 되었으면 글쓰기js파일은 삭제해버리고
컴포넌트 폴더도 삭제해버리자
그리고 프로젝트를 종료했다가 다시 실행해보자
그리고 배열안에 가상의 데이터를 제목과 내용으로 만들어서 총5개를 세팅한다
const [posts,setPosts]= useState([
{
id:1,title:"제목1",content:"내용1"
},
{
id:2,title:"제목2",content:"내용2"
},
{
id:3,title:"제목3",content:"내용3"
},
{
id:4,title:"제목4",content:"내용4"
},
{
id:5,title:"제목5",content:"내용5"
},
]);
리스트페이지안에 이렇게 만들어주자
이제 같은 파일에 있으니 제어할 수 있게 되었다
텍스트에 글을 쓰면 그 글의 내용을 핸들라이트 할 때 캐치하면된다
캐치하기위해서는 버튼을 클릭했을 때 input태그에 접근해야한다
가장 쉬운 방법은 input태그에 상태를 달아두는 것이다
인풋태그 하나를 추가해서 내용을 입력해주세요 라고 총2개를 만들고
2개 모두 상태를 넣어주자
상태를 만들기위해 아까 사용했던 posts처럼 배열을 하나 만드는데
이번에는 s를 제외한 단일로 그냥 post라고해서 하나 만들어보는데
const [post,setPost] = useState({
id:"",
title:"",
content:""
})
이렇게 하나 만들고 데이터는 공백으로 넣어준다
그 다음에 post를 제목과 내용을 입력하는 input창안에
value={post.title} value={post.content}
이런식으로
넣어주면 입력할 때 마다 value안에 값을 넣어주게되는데
setPost가 아니여서 상태가 변경되지 않기 때문에
렌더가 다시 일어나지않아서 글을 써도 input창에 출력되지 않는다
입력자체가 되지 않는다
value를 지우면 잘 작성되지만 value를 넣으면 글이 안써진다고 이해하면 된다
만약 위에 "" 이렇게 공백을 준 곳에 1이라는 값을 임의로 다 지정해주면?
이렇게 1이라고 나온다
하지만 input창에 입력을해도 1에서 바뀌지않고 입력조차 되지 않는다
이럴 경우 값을 변경하기 위해 사용하는 가장 편리한 방법은 input창 2개에
모두 onChange 이벤트를 넣어주면된다
이 이벤트는 변화가 있을경우 동작하게 되어있다
글을 쓸 경우 뭔가 변화가 일어나는지 알아보기 위해 console.log(e)를 넣어서
한번 입력해보고 콘솔에 잘 찍히는지 확인해보자
다시 위로가서
const handleChangeTitle=(e)=>{
console.log(e);
}
const handleChangeContent=(e)=>{
console.log(e);
}
이렇게 넣어주고
아래에 input창에 onChange={handleChangeContent}/>
이렇게 넣어보자
글을 쓸 때 마다 콘솔에 찍히는 것이 확인되었다
콘솔에target에 value를 누르면 "" 이렇게 찍혀있는 것이 확인되는데
그 공백을 누를때마다 갱신이 되게 하려면 어떻게 해야할까?
바로 setPost를 호출해야한다
const handleChangeTitle=(e)=>{
console.log(e.target.value);
setPost(e.target.value);
}
const handleChangeContent=(e)=>{
console.log(e.target.value);
setPost(e.target.value);
}
이렇게 넣어주면 된다
그리고 글을쓰면 console에 찍히는 것이다
여기서 치명적인 단점이 생기는데 input태그가 10개라면
함수도 10개를 만들어야한다는 치명저인 단점이다
만약 1억개라면? 함수도 1억개? 세상 불편하고 비효율적이다
이걸 함수 하나로 보내는 방법이 있다
const handleForm=(e)=>{
console.log(e.target.name);
console.log(e.target.value);
}
이렇게 하나를 만들어주고
아래에 inpu창에
onChange={handleForm} name={"content"}
이렇게 적어주는데 하나의 함수를 넣을 경우 공식은 name으로 지정해줘야한다
title 그리고 content를 전부 name으로 지정해주고 글을 입력하면
이렇게 값이 타이틀에 1 콘텐트에1 이렇게 네임과 같이 찍히는 것을 확인할 수 있다
여기서 자바에는 없는 자바스크립트에서만 사용가능한 새로운 문법을 살짝 배울 수 있었는데
그건 바로 compute property name이라는 것이다
사용해보자
// <computed property names문법 (키값 동적할당)
setPost({[e.target.name]:e.target.value});
console.log(post.title);
console.log(post.content);
이런식으로 사용해서 글을 쓰면 undefined와 함께 값이 출력되는 것을 확인 할 수 있다
물론 이해하기 아직 복잡하지만 이런것이 있다 정도로 이해하고 넘어가면 될 것 같다
자 이제 handleWrite와 handleForm을 제외하고 모두 지워버리고
<StyledItemBoxDiv>
<div>
번호:{post.id} / 제목:{post.title} / 내용:{post.content}
</div>
<button>삭제</button>
</StyledItemBoxDiv>
이렇게 안에 내용이 보이도록 내용도 하나추가해주고 / 로 구분되게 만들어주자
이제 준비가 되었으면 input창에 제목6 내용6이라고 적고 글쓰기 버튼을 클릭해보자
번호는 넣지 않았으니 번호는 공백으로 나올 것이다
그런데 이상한점이 있다 왜 내용은 나오는데 제목은 나오지 않을까?
확인을 위해 우선 post에 공백으로 두었던 id:""을 id:0으로 맞춰주고
const handleWrite= ()=>{
console.log(1,post.title);
console.log(2,post.content);
setPosts([...posts,post]);
}
이렇게 작성해서 글쓰기 버튼을 눌렀을 때 실제로 값이 잘 출력되는지 확인해보는데
글쓰기버튼을 누르니까 title은 undefined가 나온다
이유가뭘까?
타이틀이 적히고 그다음 컨텐트가 적히는데
컨텐트가 적히면서 기존의 title내용은 사라지게 된다
그래서 최종출력에서 content만 출력이 되는 것이다
수정 즉 업데이트화가 되어야지 삭제 후 출력이 되어서는 안된다
기본값이 없어서 출력하면 번호에 자꾸 0이 들어가서 보기 안좋으니
let no=6; 이라고 하나 만들어서
현재5까지 출력되었으니 6부터 출력되도록 적어주고
handleWrite안에 글쓰기가 될때마다 증가하도록
no++; 를 적어서 작성하면
이렇게 번호가 순서대로 출력된다
let no =6;
const [post,setPost] = useState({
id:no,
title:"",
content:""
})
이렇게 넣어줬고
const handleWrite= ()=>{
console.log(1,post.title);
console.log(2,post.content);
setPosts([...posts,post]);
no++;
}
이렇게 적어줬다
그런데 2개의 글을 썼더니
글이 번호가 자꾸 6으로만 나온다
해결해보자
상태값이 아니여서 변경되지 않으므로
let no=6;
이 코드를
const [no,setNo]=useState(6);
이렇게 수정해주고 기본값을 6을 준다
handleWrite로 내려가서
const handleWrite = () => {
setPosts([...posts, { ...post, id: no }]);
setNo(no + 1);
};
이렇게 수정해주면 번호가 6 7 8 9 10 이런식으로 잘 나오게 된다
전체코드를 보면
import React, {useState} from 'react';
import styled from "styled-components";
const ListPage = () => {
const StyledItemBoxDiv= styled.div`
display: flex;
justify-content: space-between;
border: 1px solid black;
padding: 10px;
margin: 10px;
height: 100px;
align-items: center;
`;
const [no,setNo]=useState(6);
const [post,setPost] = useState({
id:no,
title:"",
content:""
})
const [posts,setPosts]= useState([
{
id:1,title:"제목1",content:"내용1"
},
{
id:2,title:"제목2",content:"내용2"
},
{
id:3,title:"제목3",content:"내용3"
},
{
id:4,title:"제목4",content:"내용4"
},
{
id:5,title:"제목5",content:"내용5"
},
]);
const handleWrite = () => {
setPosts([...posts, { ...post, id: no }]);
setNo(no + 1);
};
const handleForm=(e)=>{
// console.log(e.target.name);
// console.log(e.target.value);
// <computed property names문법 (키값 동적할당)
setPost({
...post,
[e.target.name]:e.target.value
});
// console.log(post.title);
// console.log(post.content);
}
return (
<div>
<h1>리스트 페이지</h1>
<form>
<input type={"text"} placeholder={"제목을 입력해주세요..."} value={post.title}
onChange={handleForm} name={"title"}/>
<input type={"text"} placeholder={"내용을 입력해주세요..."} value={post.content}
onChange={handleForm} name={"content"}/>
<button type={"button"} onClick={handleWrite}>글쓰기</button>
</form>
<hr/>
{posts.map((post)=>
<StyledItemBoxDiv>
<div>
번호:{post.id} / 제목:{post.title} / 내용:{post.content}
</div>
<button>삭제</button>
</StyledItemBoxDiv>
)}
</div>
);
};
export default ListPage;
이렇게 되어있는데 전체흐름과 어떻게 동작하는지 정리해보자
최초에 새로고침을 하면
useState 초기값들이 세팅되어있는데
번호는 6으로 선언이 되어있고
post 는
번호
제목
내용
3개를 가지고 있는 배열이고
초기값은
번호:6
타이틀:공백
내용:공백
이렇게 세팅이 되어있다
그리고 posts라는 가상의 데이터도 세팅해줬는데
5개의 데이터가 들어있고
1번부터5번까지
제목1부터~5까지
내용1부터~5까지
전부 초기값이 세팅되어있다
그리고 제목작성 Input창과 내용작성Input창
글쓰기 버튼을 1개 만들어줬고 삭제버튼도 1개 달아줬다
함수이벤트는 총2개를 만들어줬는데
하나는 handleWrite
또 하나는 handleForm이다
form은 글을 작성할 경우 발동되도록
input창에 onChange이벤트를 줬고
write는 글쓰기를 누를때 발동하도록 onClick이벤트를 줬다
자 이렇게 세팅된 상태에서
새로고침하면 페이지에 기존의 세팅한 5개의 가상데이터를 출력해야함으로
map으로 번호,제목,내용을 출력해줬다
그 결과 새로고침해서 최초로딩이 되면
이렇게 출력이 된다
이 상태에서 제목 input창에 값을 넣으면 변화가 생기니까
onChange에 심어진 handleForm이벤트가 발동되고
setPost({
...post,
[e.target.name]:e.target.value
});
이 코드가 실행되는데
먼저 ...post로 전개연산자를 이용해서
id:no,
title:"",
content:""
이 코드가 실행되는데
제목에 a라고 넣으면
input창에 value={post.title} 와 name={"title"}
이렇게 넣었으므로
a라는 값이 들어와서
[e.target.name]:e.target.value
이 코드처럼
value는 post.title이니까 작성을 하면 a가 post에 벨류로 들어가고
네임이 타이틀이니까 타이틀에 해당 벨류가 들어간다
그 결과로
id:no,
title:a,
content:""
이렇게 세팅이 되어있고
대기하고있는 상태다
똑같이 내용input에도 b라는 값을 적어보면
<input type={"text"} placeholder={"내용을 입력해주세요..."} value={post.content}
onChange={handleForm} name={"content"}/>
이렇게 내용을 입력하면
post배열안에 content에 값이 들어가고 name이 content이므로 content에 값이 들어가서
id:no,
title:a,
content:"b"
이렇게 준비가 되어있게된다
이 상태에서 글쓰기 버튼을 누르면
onClick이벤트가 발동되서
const handleWrite = () => {
setPosts([...posts, { ...post, id: no }]);
setNo(no + 1);
};
handleWrite이벤트가 발동되는데
코드를 보면
먼저 setPosts를 이용해서 변화를 줄 수 있다
최초로 ...posts로 가상의 5개의 데이터를 뿌려주고
뿌려진 다음
...post로 아까 준비된 post배열을 뿌려주고 id:no라고 선언해준다
그럼 출력되는 것은 no은 6이니까 번호는 6이되고 아까 입력한 a와 b가 나온다
그리고 다음줄에보면 setNo를 통해 no상태를 변경할 수 있는데 no+1을 줬으니
6더하기1 즉 7이 되서 어딘가에 잔존하게 되고
다음에 글을쓰면 번호는 이미 7이 되어있으니 번호가 7번이 출력되고
제목과 내용에 작성한 데이터가 출력되고 다시번호는 8이되고 어딘가에 잔존하고
이렇게 반복해서 출력되면 6 7 8 9 10 11 12 순서대로 출력되는 것이다
자 한번 더 확실히 정리하면
const handleForm=(e)=>{
// <computed property names문법 (키값 동적할당)
setPost({
...post,
[e.target.name]:e.target.value
});
}
위 코드는 input창에 변화가 생겼을 때 onChange이벤트가 발동되서 실행되는데
최초로
const [post,setPost] = useState({
id:no,
title:"",
content:""
})
초기값을 useState로 이렇게 지정해줬는데 그 위에서
const [no,setNo]=useState(6);
no는 6이라고 지정해줬기때문에
post라는 것은 현재 초기값이
id:6
title:""
content:""
이렇게 되어있다
그래서 input창에 값을 넣어서 발동이되면 ...post로 전개연산자로 뿌려주는데
타겟네임은 타켓벨류다 라고 코드를
[e.target.name]:e.target.value
적어줬기 때문에
input창 코드에 보면
value={post.title}이렇게 적어줬는데 이 말은 즉슨 value안에
"abcd"라고 적어주면
절대 변하지않는 값으로 abcd가 들어간다
value라는게 그런 속성을 가지고 있는 것이다
우리는 이곳에
value={post.title} 이렇게 적어줬는데
useState로 "" 공백을 줬기 때문에 클릭하고 글을 쓸 수 있고
placeholder에 적어뒀던 "내용을 입력해주세요..." 도 input창 클릭전에
노출되는 것이다 플레이스홀더는 박스안에 클릭전까지 띄어줄 문자를 넣는 속성이다
초기세팅이 공백이니까 공백으로 나오고 거기에 값을 넣을 때
아래 코드를 다시 보자
[e.target.name]:e.target.value
이게 무엇이냐면
이벤트의 타겟이 네임이 무엇이냐? 나는 name={"title"} 이라고 줬으니까
title이라는 것을 찾고 title: 이렇게 준비해주고
value가 내가 적은 텍스트니까 a라고 적었다면?
title: "a" 이렇게 들어가는 것이다
그럼 초기세팅은
no: 6
title: a
content : "" 공백
이렇게 세팅이되어있고
content도 그대로 똑같은 원리로 작성해서 만약 인풋창에 b라고 적었다면
초기세팅은
no:6
title:a
content:b
이렇게 머물고 있다
이 상태에서 글쓰기 버튼을 누르면 onClick이벤트가 발동되어서
handleWrite함수가 실행되는데
const handleForm=(e)=>{
// <computed property names문법 (키값 동적할당)
setPost({
...post,
[e.target.name]:e.target.value
});
}
const handleWrite = () => {
setPosts([...posts, { ...post, id: no }]);
setNo(no + 1);
};
위 코드를 제대로 이해해보면 먼저 발동될 때 setPosts가 나온느데
이 녀석은 posts라는 것을 변경할 수 있는 녀석이다
먼저 ...posts로 초기 임의데이터 5개를 전개연산자로 뿌려준다
그럼 번호1부터 5까지 총 5개의 map이 똑같이 출력되고
그다음 코드를 보면 ...post로 post를 뿌려주는데
이 post가 뭐냐?
아까 세팅해뒀던
no:6
title:a
content:b
요 녀석이다 그럼 요게 출력이 되는데 여기서 id는 no 라고 한번더 명시해줬다
그 이유는 id : no 가 없으면
출력은 되는데 계속 6번만 나온다 왜냐면 초기값이 6이니까
이 함수에서는 초기값만 계속뽑아오지 변화가 일어나지않으면 원래 초기값만
useState에서 적힌 6으로 무한정뽑아낸다
그걸 막기위해 id:no라고 해주고
아래 코드에서 setNo를 이용해서 no를 컨트롤하게 해주고
어떤 컨트롤이냐 헨들러라이트 함수가 발동될 때 마다 숫자 1을 더해줘라 라고 한 것이다
그럼 6번이 나오고 바로 6더하기1 해서 7번을 어딘가에 보관하고 있는 것이다
그 상태에서 또 글을 작성하면 id:no이니까 초기상태는 7이 되서 7번이 나오고
7번 더하기 1 이되어서 8번을 또 만들고 보관했다가 계속 가져와서
순서대로 6 7 8 9 10 이렇게 불러올 수 있는것이다 !
그런데 기본적으로 버튼 타입을 버튼이 아니라 submit을 쓸 수 있다
그래서 우리는 어떻게 할 것인가?
바로 버튼 안에 type을 submit을 주고
온클릭 이벤트를 지우고 form 안에 이벤트를 넣어주면 submit이 되면서
바로 form에서 이벤트가 걸리게 된다
하지만 이렇게하면 단점이 하나 있는데
바로 submit이 될 때 새로고침이 되는 액션이 있다는 것이다
그것을 막아주는 것 까지 해볼 생각이다
우선 버튼 타입을 submit으로 바꿔주고
onClick을 지워주고 form 태그로 가서
onSubmit 안에 해당 이벤트를 적어서 넣어준다
그리고
const handleWrite = (e) => {
e.preventDefault(); //form태그가 하려는 액션을 중지시켜야함
setPosts([...posts, { ...post, id: no }]);
setNo(no + 1);
};
이런식으로 () 안에 e 를 넣어서 이벤트를 인식시켜주고
새로고침이 되지 않도록 e.preventDefault를 넣어서 방지해주면
정상적으로 submit을 사용할 수 있다 !