어제 context 를 사용해서 만든 댓글 추가 기능
삭제하기
CommentList.jsx
const Item = list.map((v, k) => {
return (
<>
<CommentItem
key={k} // props 가 아니야
userid={v.userid}
content={v.content}
data={v.date}
index={k} // kdy값 따로 보내기
/>
CommentItem.jsx
import React from 'react'
const CommentItem = ({userid,content,date, index}) => {
return (
이제 Context 사용하기
Form, List는 Context 사용한 상태
이제 Item에도 사용하기 -> useContext가져오기
import React, {useContext, useState} from 'react'
import Stroe from './store/context'
const CommentItem = ({userid,content,date, index}) => {
const {state,dispatch} = useContext(Store)
const [input,setInput] = useState('')
return (
<>
<ul className="comment-row">
<li className="comment-di">{userid}</li>
<li className="comment-content">{content}</li>
<li className="comment-date">{date}</li>
</ul>
</>
)
}
export default CommentItem
삭제 버튼 만들고 click 이벤트 넣기
import React, {useContext, useState} from 'react'
import Store from './store/context'
const CommentItem = ({userid,content,date, index}) => {
const {state,dispatch} = useContext(Store)
const [input,setInput] = useState('')
const handleDelete =()=>{
dispatch({type:'DELETE', payload:index,})
setInput('')
}
return (
<>
<ul className="comment-row">
<li className="comment-di">{userid}</li>
<li className="comment-content">{content}</li>
<span className="comment-delete-btn" onClick={handleDelete}> {/* 삭제 버튼 */}
X
</span>
<li className="comment-date">{date}</li>
</ul>
</>
)
}
export default CommentItem
reducer.jsx 에서 받은 DELETE 부분 수정
* 요기서 console.log 이것저것 쳐보기 !
// reducer가 바꿀 상태 state -> 이전 상태값 가져와야함
// dispatch가 바꿀 정보를 줄꺼야 -> 두번째 인자값 action으로 받음
// 최종적으로 reducer는 수정된 state 값을 보내줌
const reducer = (state, action) => {
console.log('action',action)
switch (action.type) {
case 'CREATE':
return {
...state, // 다른 값들 넣을 때 대비해서 일단 넣어두기
commentItem:[...state.commentItem, action.payload]
}
case 'UPDATE':
return {
...state,
}
case 'DELETE':
console.log(action, action.payload)
console.log(state)
console.log(state.commentItem)
return {
...state,
commentItem:[...state.commentItem.filter((v,k)=>action.payload !==k)]
}
default:
return{
...state,
}
}
}
export default reducer
for문
case 'DELETE':
console.log(action, action.payload)
console.log(state)
console.log(state.commentItem)
let new_arr = []; //slice 도 써보기 // payload.index도 찍어보기
for(let i=0; i<state.commentItem.length; i++){
console.log(i)
if(i != action.payload)
new_arr.push(state.commentItem[i])
}
//commentItem의 내용을 new_arr로 바꿔주기
return {
...state,
commentItem:new_arr
}
default:
return{
...state,
}
}
수정하기
commentItem.jsx 에서 {context}가 담긴 부분 클릭했을 때 input box 열리도록 만들 것! -> {content}를 클릭 이벤트 주기 편하게 span으로 감싸고 handleClick 이벤트 추가 + onChange
CommentItem.jsx
import React, { useContext, useState } from 'react'
import Store from './store/context'
const CommentItem = ({ userid, content, date, index }) => {
const { state, dispatch } = useContext(Store)
const [input, setInput] = useState('')
const handleDelete = () => {
dispatch({ type: 'DELETE', payload: index, })
setInput('')
}
const handleClick=()=>{
setInput(content)
}
const handleChange=(e)=>{
setInput(e.target.value)
}
return (
<>
<ul className="comment-row">
<li className="comment-di">{userid}</li>
<li className="comment-content">
<span onClick={handleClick}>
{/* {content} */}
{input ? <input type="text" value={input} onChange={handleChange} /> : content}
</span>
</li>
{/* 삭제 버튼 */}
<span
className="comment-delete-btn"
onClick={handleDelete}
>
X
</span>
<li className="comment-date">{date}</li>
</ul>
</>
)
}
export default CommentItem
input 입력 다하고 엔터 키를 누를 때 dispatch 실행
const handleKeydown=(e)=>{
console.log('e=',e)
console.log('e.key=',e.key)
if (e.key=='Enter'){
dispatch({type:'UPDATE', payload:{index,content:input,}})
setInput('')
}
}
return (
<>
<ul className="comment-row">
<li className="comment-di">{userid}</li>
<li className="comment-content">
<span onClick={handleClick}>
{/* {content} */}
{input ? <input type="text" value={input} onChange={handleChange} onKeyDown={handleKeydown}/> : content}
</span>
</li>
reducer.jsx 수정
여기서 console.log 찍어보니까 rerender 할 때 layout, form, 다 재 렌더된다.
const reducer = (state, action) => {
console.log('action',action)
switch (action.type) {
case 'CREATE':
return {
...state, // 다른 값들 넣을 때 대비해서 일단 넣어두기
commentItem:[...state.commentItem, action.payload]
}
case 'UPDATE':
console.log('UPDATE 도착햇ㄷㅏ.',action)
let {content, index} = action.payload // == {...action.payload}
console.log(content,index)
let {commentItem} = state // {...state}
commentItem[index].content = content
return {
...state,
conmmentItem:[...commentItem] // == commentItem, 만 써도 됨
}
위의 코드를 똑같이 아래도 있,,
근데 비구조 할당문으로 가져오는거 {...} [...] 요 부분이 헷갈린다.
return 부분에
state 만 쓰면 X
...state 만 써도 ok
...state 없이 commentItem, 만 써도 ok
...state, commentItem, 써도 ok
...state, commentItem:[...commentItem] 도 ok
state, commentItem 을 쓰면 ㄴㄴ !
case 'UPDATE':
console.log('UPDATE 도착햇ㄷㅏ.',action)
// let {content, index} = action.payload // == {...action.payload}
// console.log(content,index)
// let {commentItem} = state // {...state}
// commentItem[index].content = content
// return {
// ...state,
// conmmentItem:[...commentItem]
// }
let {content,index} = action.payload
let commentItem = state.commentItem // 복사해서 가지고 오기
console.log(commentItem)
console.log(commentItem[index])
console.log(commentItem[index].content)
commentItem[index].content = content
return{
...state,
commentItem:[...commentItem]
}
수정하기 , 삭제하기는 기본이니까 외워도도 좋다고 !!! 외우장.. 아니면 to do list를 만들어 봐도 좋을듯
< 리액트 로그인하기 전 개념 설명 TIME=3 >
웹팩의 역할 -> app.js를 만들어주기
run dev -> app.js 파일에 모든 파일 담은 index.html (브라우저)를 출력
ren dev -> 개발 서버 html을 브라우저에 띄울 수 있게끔 만듬 (하나의 서버이긴 함)
fetch가 실행되면 브라우저가 node.js (백엔드)로 요청함 (개발 서버 run dev 는 관련이 없다! )
개발 서버는 index.html (with app.js, app.css) 만 브라우저에 던져줄뿐,,,
순수히 브라우저에서 동작하는 js는 파일을 읽고 쓰고 보내고 기능이 전혀 없다.
유일하게(?) 가능했던 대표적인 사례는 공인인증서 ( ActiveX ) -> 보안을 위해 도입되었지만 결국 보안 문제가 야기됨
브라우저(8080)에서 백엔드- port:3000으로 요청이 될까 ? ---------> 안됨 (cors 문제)
브라우저의 통신은 같은 도메인( port ) 이어야 가능하다. - > 근데 친구가 되면 가능하다고(?)
-> 우리의 폴더 작업은 webpack 설치된 리액트 폴더 + 원래 사용했던 express, node.js server + DB server 총 서버는 3개가 쓰인다.
터미널 두개 만들 수 있다.
이름 front, back으로 수정하고
1. backend 폴더 만들기 (webpack front것 밖으로)
terminal 위치 조정 -> backend 폴더로 들어가기
npm init
npm i express cors
server.js 파일 만들고 기본 코드 작성
오랜만이닷..
const express = require('express')
const app = express()
app.get('/', (req,res)=>{
res.send('hi')
})
app.listen(3000,()=>{
console.log(`server port : 3000`)
})
리액트 하다가 요거 치니깐 친정 온 기분..
2. get('/api') 로 만들어서 json 정보 작성
const express = require('express')
const app = express()
app.get('/api', (req,res)=>{
res.json({
userid:'아이디',
content:'댓글',
date:'2021-07-15',
})
})
app.listen(3000,()=>{
console.log(`server port : 3000`)
})
3. 최초로 컴포넌트가 실행되었을 때 (render) console.log 찍기 - > useEffect
useEffect는 Hooks API 의 생명 주기 (mount...이거)
terminal front - npm run dev 해놓기 !
리액트 - CommentLayout.jsx
import React, { useContext, useReducer, useEffect } from 'react'
import Store from './store/context'
import reducer from './store/reducer'
const CommentLayout = ({ children }) => {
const globalStore = useContext(Store) // context 사용할 때는 use
//여기서 인자값 Store 들어오는 거 잘 분석하기 !
// store 에서 export 하는거 내용 잘 봐
console.log(globalStore)
const [state, dispatch] = useReducer(reducer, globalStore)
console.log('state value = ', state)
useEffect(()=>{
console.log('최초 실행 render')
},)
처음 실행되었을 때 server에 내용 요청할 예정 !
서버에 요청하는 코드 작성
useEffect(async()=>{
console.log('최초 실행 render')
//fetch 결과값 : Promise -> 비동기
const response = await fetch('http://localhost:3000/api')
const data = await response.json()
console.log(response)
console.log(data)
},)
도메인 다르다는 오류가 나옴 ! 친구인 걸 증명해야함
4. 백엔드로 server.js 로 가기
const express = require('express')
const app = express()
const cors = require('cors')
app.use(cors()) // 미들웨어에 실행시켜주기
app.get('/api', (req, res) => {
// 데이터는 DB에 접속해서 select -> 객체로 만들어서 응답 주기
res.json([{
userid: '아이디',
content: '댓글',
date: '2021-07-15',
}])
})
app.listen(3000, () => {
console.log(`server port : 3000`)
})
backend server off -> on
이제 둘이 친구가 됐다.
5. context.jsx 의 객체를 지우기
import React from 'react'
const initialState ={
commentItem:[ ]
}
6. 이제 fetch로 받은 data로 내용 채우기
useEffect(async()=>{
console.log('최초 실행 render')
//fetch 결과값 : Promise -> 비동기
const response = await fetch('http://localhost:3000/api')
const data = await response.json()
console.log(response)
console.log(data)
dispatch({type:'INIT', payload:data})
},)
reducer로 가서 INIT 생성, 코드 추가
7. reducer.jsx
1) 방법 1
const reducer = (state, action) => {
console.log('action',action)
switch (action.type) {
case 'INIT':
return {
...state,
commentItem:action.payload
}
2) 방법 2
case 'INIT':
state.commentItem = action.payload
return {
...state,
// commentItem:action.payload
}
8. comment > api 폴더 > api.jsx 파일 만들기
비동기 코드를 다 빼볼 것 ! - > 함수만 쓸 예정
코드 작성 (아까 CommentLayout.jsx에 쓴 fetch 코드를 따로 빼기)
api.jsx
//비동기 함수들 모음
export const getComment = async (dispatch) =>{
dispatch({type:'GET_COMMENT'})
try{
const response = await fetch('http://localhost:3000/api')
const data = await response.json()
dispatch({type:'GET_COMMENT_SUCCESS', payload:data})
}catch(e){
dispatch({type:'GET_COMMENT_ERROR', payload:e})
}
}
9. Store > context.jsx 에 상태값 두 가지 추가
import React from 'react'
const initialState ={
commentItem:[ ],
loadding: false,
error:null,
}
const Store = React.createContext(initialState) // context 생성
// 인자값으로 default값
export default Store
10. reducer 에서 받을거니까 수정 !
reducer.jsx
const reducer = (state, action) => {
console.log('action', action)
switch (action.type) {
case 'GET_COMMENT': //최초 실행 했을 때
return {
...state,
loadding:true
}
case 'GET_COMMENT_SUCCESS': // 로딩 끝났다 ~
return {
...state,
loadding:false,
commentItem:action.payload
}
case 'GET_COMMENT_ERROR': //에러 났다 ~
return {
...state,
error:action.payload
}
로딩 중일 때 loadding:true 이고 get_comment_success가 실행되면 (data 잘 가져와서 뿌리면 ) 로딩 false로 종료 !
11. CommentLayout.jsx
함수 파일 불러오기, 아래 코드들을 함수로 대체하기, 함수 매개변수 dispatch 넣기 !
import React, { useContext, useReducer, useEffect } from 'react'
import Store from './store/context'
import reducer from './store/reducer'
import {getComment} from './api/api'
const CommentLayout = ({ children }) => {
const globalStore = useContext(Store) // context 사용할 때는 use
//여기서 인자값 Store 들어오는 거 잘 분석하기 !
// store 에서 export 하는거 내용 잘 봐
console.log(globalStore)
const [state, dispatch] = useReducer(reducer, globalStore)
console.log('state value = ', state)
useEffect(async()=>{
console.log('최초 실행 render')
//fetch 결과값 : Promise -> 비동기
// const response = await fetch('http://localhost:3000/api')
// const data = await response.json()
// console.log(response)
// console.log(data)
// dispatch({type:'INIT', payload:data})
getComment(dispatch)
},)
12. CommentList.jsx
import React, { useContext, useState } from 'react'
import CommentItem from './CommentItem'
import Store from './store/context'
const CommentList = () => {
const { state } = useContext(Store) // {state, dispatch}
const list = state.commentItem
const {loadding, commentItem, error} = state
const Item = list.map((v, k) => {
return (
<>
<CommentItem
key={k} // props 가 아니야 내용 채울 때 속상값. 안쓰면 key값 오류남
userid={v.userid}
content={v.content}
data={v.date}
index={k} // kdy값 따로 보내기
/>
</>
)
})
if(loadding) return <li>나 로딩 중 </li>;
if(error) return <li>에러 났다능..</li>;
return (
<li>
{Item}
</li>
)
}
export default CommentList
Network -> 저 위 부분 클릭하면 조금 느려짐 !! -> 그럼 새로고침하면 ' 나 로딩중..' 이 뜸
fetch 주소 잘못 쓰면 에러가 뜸 !
'블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기' 카테고리의 다른 글
[89일차] Amazon AWS 웹서버 구축 / 호스팅/ 배포 설정 비용 (0) | 2021.07.16 |
---|---|
[88일차 복습] 리액트 댓글 수정 삭제 (0) | 2021.07.15 |
[87일차 복습] 리액트 Context 사용해서 댓글 추가하기 (0) | 2021.07.14 |
[87일차]20210714 Context로 댓글 만들기 (with 리액트식으로 컴포넌트 구성하기) (0) | 2021.07.14 |
[86일차 복습] React useReducer Context syled-component로 TicTacToe만들기 (0) | 2021.07.14 |