[무한 스크롤] 게시판 리스트 출력 [2] - 스크롤 구현 후 포커싱 유지
2023. 7. 10. 20:52ㆍGithub 프로젝트/scroll구현연습
728x90
반응형
SMALL
이번시간에는 게시판을 활용하여 무한 스크롤을 진행하고, 포커싱 유지하는 법을 다뤄본다.
무한 스크롤은 페이지 이동시 state 값이 다시 리렌더링이 되기 때문에 이전화면으로 돌아가서 이미 내려간 스크롤 위치를 기억하기 쉽지 않기 때문에 모달을 이용하여 포커싱 유지하는 것으로 꼼수를 쓴다.
이 프로젝트를 실습할 때에는 ex 디렉토리로 들어가서 front부분을 제외한 나머지는 sql 작업과 back-end 서버를 그대로 구동 시킨 후 front-end에 있는 App.js에서 실습을 시행합니다.
Github 저장소 : https://github.com/BerkleyLim/scroll_project
1. 사전 준비
먼저 Github에 있는 샘플 파일 ex 디렉토리에서 가져와 활용을 해본다.
다음으로는 scroll_projet_front/src/App.js를 열어 무한 스크롤을 이용하여 페이징 처리를 진행하도록 한다.
데이터는 scroll_project_sql 에서 database를 넣어주고, scroll_project_back 까지 실행해주어야 데이터가 들어온다.
2. 스크롤 구현
다음으로는 "react-intersection-observer" 를 이용하여 무한 스크롤 페이징 처리를 진행해본다.
1) 먼저 "react-intersection-observer" 라이브러리 중 하나인 useInView()를 정의해준다.
const [ref, isScroll] = useInView();
2) 다음으로는 기존에 useEffect에 첫번째 렌더링 했을때 보여주는 페이지를 제거해준다.
(필자는 코딩 흔적을 남기기 위해 주석 처리 해 주었다.)
// 첫 조회
// useEffect(() => {
// console.log(limit + " " + offset)
// axios
// .get("http://localhost:8080/api/board/select/list?pageNumber=" + limit + "&startPage=" + offset+limit)
// .then((res) => {
// setBoard(res.data);
// console.log(res.data);
// setOffset(limit+offset);
// })
// .catch((e) => {
// console.error(e);
// });
// }, []);
3) 다음은 아래와 같이 스크롤을 위해 map 아래에 <div ref={ref}>데이터없음</div> 을 추가해준다.
이때, ref는 무한 스크롤을 위한 기능으로 "react-intersection-observer" 라이브러리 활용으로 수행해준다.
{board?.map((b, index) => (
<Card key={index} className="my-2" color="primary" outline>
........
</Card>
))}
<div ref={ref}>데이터 없음</div>
4) 2)번의 useEffect()를 대신해준 것을 linitPaging 메서드를 정의하면서 데이터를 받아온다.
const limitPaging = () => {
axios
.get(
"http://localhost:8080/api/board/select/list?pageNumber=" +
limit +
"&startPage=" +
(offset +
limit)
)
.then((res) => {
setBoard([...board, ...(res.data)]);
console.log(res.data);
setOffset(limit + offset);
})
.catch((e) => {
console.error(e);
});
};
5) 마지막으로 데이터 렌더링할 useEffect를 실행해준다.
useEffect(() => {
// isScroll이 true 일때만 실행
if (isScroll) {
// 실행할 함수
limitPaging();
}
}, [isScroll]);
// 무한 스크롤 로직 끝
6) 여기까지 하셨으면 렌더링이 되지 않습니다. 이때 board에 초기 값을 배열로 지정합니다.
const [board, setBoard] = useState([]);
7) 최종 결과물
import {
Card,
CardHeader,
CardBody,
CardText,
Button,
Row,
Col,
Input,
} from "reactstrap";
import "./App.css";
import { useEffect, useRef, useState } from "react";
import axios from "axios";
import update from "immutability-helper";
import ModalComponent from "./modal/ModalComponent";
import { useInView } from "react-intersection-observer";
function App() {
const [board, setBoard] = useState([]);
const [message, setMessage] = useState();
const [mode, setMode] = useState("create");
const [updateData, setUpdateData] = useState();
const [updateIndex, setUpdateIndex] = useState();
const [isStateChange, setIsStateChange] = useState(false);
const [isModal, setIsModal] = useState(false);
const limit = 5;
const [offset, setOffset] = useState(0);
const [ref, isScroll] = useInView();
const modalViewToggle = () => setIsModal(!isModal);
// 무한 스크롤 로직
const limitPaging = () => {
axios
.get(
"http://localhost:8080/api/board/select/list?pageNumber=" +
limit +
"&startPage=" +
(offset +
limit)
)
.then((res) => {
setBoard([...board, ...(res.data)]);
console.log(res.data);
setOffset(limit + offset);
})
.catch((e) => {
console.error(e);
});
};
useEffect(() => {
// isScroll이 true 일때만 실행
if (isScroll) {
// 실행할 함수
limitPaging();
}
}, [isScroll]);
// 무한 스크롤 로직 끝
// 첫 조회
// useEffect(() => {
// console.log(limit + " " + offset)
// axios
// .get("http://localhost:8080/api/board/select/list?pageNumber=" + limit + "&startPage=" + offset+limit)
// .then((res) => {
// setBoard(res.data);
// console.log(res.data);
// setOffset(limit+offset);
// })
// .catch((e) => {
// console.error(e);
// });
// }, []);
// 삽입
const readyChange = () => {
setMessage("추가");
setMode("create");
modalViewToggle();
};
const createBoard = (data) => {
// front 화면에서 생성하는 것처럼 행동
setBoard(
update(board, {
$push: [data],
})
);
// 이후 api 호출 후 데이터 삽입
axios
.post("http://localhost:8080/api/board/insert/board", data)
.then((res) => {
alert("생성 성공!!");
console.log(res);
})
.catch((e) => {
console.error(e);
});
modalViewToggle();
};
// 삽입 끝
// 삭제 시작
const deleteBoard = (index) => {
const deleteData = board[index];
setBoard(
update(board, {
$splice: [[index, 1]],
})
);
// 이후 api 호출 후 데이터 삭제
axios
.post("http://localhost:8080/api/board/delete/board", deleteData)
.then((res) => {
alert("삭제 성공!!");
console.log(res);
})
.catch((e) => {
console.error(e);
});
};
// 삭제 끝
// 수정 시작
const readyUpdate = (data, index) => {
setMessage("수정");
setMode("update");
modalViewToggle();
setUpdateData(data);
setUpdateIndex(index);
};
const updateBoard = (data, index) => {
setBoard(
update(board, {
$merge: { [index]: data },
})
);
modalViewToggle();
setIsStateChange(!isStateChange);
// 이후 api 호출 후 데이터 수정
axios
.post("http://localhost:8080/api/board/update/board", data)
.then((res) => {
alert("수정 성공!!");
console.log(res);
})
.catch((e) => {
console.error(e);
});
};
// 수정 끝
return (
<div className="container">
<br />
<br />
<br />
<h1>무한 스크롤 삽입</h1>
<br />
<br />
<br />
{board?.map((b, index) => (
<Card key={index} className="my-2" color="primary" outline>
<CardHeader>{b?.title}</CardHeader>
<CardBody>
<CardText>{b?.contents}</CardText>
<br />
<br />
<Row>
<Col sm={{ offset: 3, size: "auto" }}>
<Button
color="primary"
onClick={() => {
readyUpdate(b, index);
}}
>
수정
</Button>
</Col>
<Col sm={{ offset: 4, size: "auto" }}>
<Button color="danger" onClick={() => deleteBoard(index)}>
삭제
</Button>
</Col>
</Row>
</CardBody>
</Card>
))}
<div ref={ref}>데이터 없음</div>
<br />
<br />
<br />
<br />
<Row>
<Button
color="success"
onClick={() => {
readyChange();
}}
>
추가
</Button>
</Row>
<ModalComponent
isModal={isModal}
modalViewToggle={modalViewToggle}
createBoard={createBoard}
message={message}
mode={mode}
updateData={updateData}
updateIndex={updateIndex}
updateBoard={updateBoard}
/>
</div>
);
}
export default App;
728x90
반응형
LIST
'Github 프로젝트 > scroll구현연습' 카테고리의 다른 글
[무한 스크롤] 게시판 리스트 출력 [1] - 사전 준비 (0) | 2023.07.08 |
---|