๊ฐ์ ๋งก์ ๋ถ๋ถ์ ๋ํด์ ๊ตฌํํ๋ ์๊ฐ์ ๊ฐ์ก๋ค.
๋๋ search history ๋ถ๋ถ์ ๋งก์๋ค. ์๊ตฌ์ฌํญ์
์ฐ๋ฆฌ๋ search history๋ฅผ ์ ์ญ์ผ๋ก ๊ด๋ฆฌํ๊ธฐ ์ํด store, reducer๋ฅผ ์์ฑํด๋์์๋ค.
// searchSlice.js
import { createSlice } from '@reduxjs/toolkit';
const searchSlice = createSlice({
name: 'search',
initialState: [],
reducers: {
updateHistory: (state, action) => {
return [...action.payload];
},
});
export const { updateHistory } = searchSlice.actions;
export default searchSlice.reducer;
์ปดํฌ๋ํธ ์์์ ์๊ตฌ์ฌํญ ์กฐ๊ฑด์ ๋ง์กฑํ๋๋ก ๋ฐฐ์ด์ ์์ฑํ๊ณ update ์ํค๋ action ํ๋๋ง์ ์์ฑํ์๋ค.
// Input.jsx
import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux';
import { deleteHistory, replaceHistory, updateHistory } from '../reducer/searchSlice';
// search history ๋ณด์ฌ์ค ์ต๋ ๊ฐ์
const historyMax = 5;
export default function Input() {
const history = useSelector(state => state.searchSlice);
const dispatch = useDispatch();
const [value, setValue] = useState("");
const onChange = (e) => {
setValue(e.target.value);
}
const onEnterDown = (e) => {
if (e.key === "Enter") {
searchFunc();
}
}
function searchFunc() {
if (history.includes(value)) {
// ๊ฐ์ ๊ฐ์ด ์์ ๋ ์ต์๋จ์ผ๋ก ์ฌ๋ฆฐ๋ค.
let temp = [...history];
temp.splice(history.indexOf(value), 1);
dispatch(updateHistory([value, ...temp]));
} else {
if (history.length === historyMax) {
dispatch(updateHistory([value, ...history.slice(0, history.length-1)]));
} else {
dispatch(updateHistory([value, ...history]))
}
}
}
return (
<div>
<input onChange={onChange} onKeyDown={onEnterDown}/>
<div> {history.map((value, i) => (<p key={i}>{i} : {value}</p>))}</div>
</div>
)
}
์ํฐ ํค๋ฅผ ๋๋ฅด๋ฉด ๊ฒ์์ด๊ฐ ์ ์ฅ๋ ์ ์๋๋ก onEnterDown
ํธ๋ค๋ฌ๋ฅผ ์์ฑํ๊ณ , ์ฌ์ฉ์๊ฐ ๋๋ฅธ ๊ฒ์ด ์ํฐ ํค์ด๋ฉด searchFun
์ ์คํํ ์ ์๋๋ก ํ๋ค.
๋์ค์ ์ฝ๋๋ฅผ ํฉ์น ๋์ ์ฉ์ดํจ๊ณผ ์ฌ์ฌ์ฉ์ฑ์ ๊ณ ๋ คํด ํจ์๋ก ๋นผ๋ณด์๋ค.
์ด์ ์กฐ๊ฑด๋ฌธ์ ๋ฐ์ ธ๋ณด์๋ค.
๊ฒ์ํ ๊ฐ ์ค ๊ฐ์ ๊ฐ์ด ์์ ๋
store๋ก ๊ด๋ฆฌ๋๊ณ ์๋ ์ํ๋ immutable ํด์ผํ๊ธฐ ๋๋ฌธ์ ๋ณ์ temp๋ฅผ ๋ง๋ค์ด์ฃผ์๋ค.
ํ์ฌ ์
๋ ฅํ ๊ฐ์ด ๋ค์ด์๋ ์ธ๋ฑ์ค๋ฅผ ์ฐพ์ splice()
๋ก ์์ ์ฃผ๊ณ , ๊ทธ ์ํ๋ฅผ ๊ทธ๋๋ก update ํด์ค๋ค.
์ต๋ ๊ฒ์์ด ๊ฐ์์ ๋๋ฌํ์ ๋
๊ฒ์์ด ๊ฐ์๊ฐ historyMax
์ผ ๋, ๊ฐ์ฅ ์ค๋๋ ๊ฒ์์ด๋ฅผ ์ญ์ ํ๊ณ ์๋ก ๊ฐ์ ๋ฃ์ด์ฃผ์ด์ผํ๋ค.
์ด ๋ํ history
๋ immutable ํด์ผํ๋ฏ๋ก splice()
๊ฐ ์๋ slice()
ํตํด ์์ ๋ณต์ฌ๋ฅผ ํด์ฃผ์๋ค.
์๋ ๊ฒฝ์ฐ (์ต๋ ๊ฐ์๋ณด๋ค ์ดํ์ด๊ณ , ์๋ก์ด ๊ฒ์์ด์ผ ๊ฒฝ์ฐ)
๊ทธ๋๋ก update ํด์ค๋ค.
๋ ๋๋ง ํ ๋ ์ฃผ์ํ ์ ์
key ๊ฐ์ด ์์ด์ผ ํ๋ค.
๋ฑํ ์ด๋ค id ๊ฐ์ด ์๊ธฐ ๋๋ฌธ์ ๊ทธ๋ฅ i(index)๋ก key ๊ฐ์ ์ง์ ํด์ฃผ์๋ค.
์ญ์์ผ๋ก ๋ณด์ฌ์ฃผ์ด์ผ ํ๋ค. (์ต๊ทผ ๊ฒ์์ด๊ฐ ์์ ์๊ฒ)
input์ด ๊ณ์ ๋ค์ด์ค๊ธฐ ๋๋ฌธ์ ์ ์ด์ ์ํ๋ฅผ ๋ณ๊ฒฝํ ๋ ์ฒ๋ฆฌํด์ฃผ์๋ค.
์ด์ ์ฌ๊ธฐ์ ๊ณ ๋ฏผ์ด ๋์๋ ๋ถ๋ถ์, ์ด๋ ๊ฒ action์ ํ๋๋ง ์ฐ๊ณ ๋ชจ๋ ์กฐ๊ฑด ์ฒ๋ฆฌ๋ฅผ ์ปดํฌ๋ํธ์์ ํด์ฃผ๋ ๊ฒ์ด ๋ง๋ ํ๋ ์๊ฐ์ด ๋ค์๋ค.
์ฒ์์ ๋ฐ๋ก ์์ฒ๋ผ ์์ฑํ ๊ฒ์ด ์๋๋ผ ์ญ์ ํ๋ action์ด ํ์ํ์ง ์๋? ์ถ์ด์ ์ญ์ ํ๋ action์ ์์ฑํ์๋๋ฐ, ๋ ๊ทธ๋ ๊ฒ ์์ฑํ๋๊น ์ด ๋ฐฉ๋ฒ์ด ๋ง๋ ์ถ์ ๊ฑฐ๋ค.
๊ทธ๋์ ํ์๋ค๊ณผ ๋งค๋์ ๋์๊ฒ ๋ฌผ์ด๋ดค๋ค.
-> middleware๋ฅผ ๋ง๋ค์ด ๊ทธ ์์์ ์กฐ๊ฑด ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ๋ ๊ฒ์ ์ด๋ค๊ฐ?
-> middleware์ ๊ฐ๋
์ด ์ด๋ ต๋ค๋ฉด, updateHistory action ๋ด์์ ๋ค ์ฒ๋ฆฌํ๋๋ก (reducer ์์์ ๋ชจ๋ ์ฒ๋ฆฌํ๊ฒ)
์ฌ์ฌ์ฉ์ฑ์ ๊ณ ๋ คํด์ reducer ๋ด์์ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ๋ ๊ฒ์ด ๋ง๋ค๋ ํ๋จ์ ๋ด๋ ค์ฃผ์
จ๋ค.
(ํ์ง๋ง middleware... ๋๋ฌด ๋ฉ๊ณ ๋ฉ๋ค.)
์ด์ฌํ ์ฐพ์๋ด์ ๊ณ ์ณ๋ณด๋ ์ค.
์๋๋ ์ก์ ์ ์ฌ๋ฌ ๊ฐ ๋ง๋ค์ด ๊ณ ์ณ๋ณธ ๊ฒ์ด๋ค.
// searchSlice.js
import { createSlice } from '@reduxjs/toolkit';
const searchSlice = createSlice({
name: 'search',
initialState: [],
reducers: {
updateHistory: (state, action) => {
// ์ญ์์ผ๋ก ๋ฃ์ด์ฃผ๊ธฐ
return [action.payload, ...state];
},
deleteHistory: (state) => {
// **
// ๋งจ ๋ค ์์ ์ญ์ ํ๋ action
return [...state.slice(0, state.length-1)];
},
replaceHistory: (state, action) => {
// **
// ๊ฐ์ ๊ฐ์ด ๋ค์ด์จ ๊ฒฝ์ฐ ์์น๋ฅผ ๋ฐ๊ฟ์ฃผ๋ action
let temp = [...state];
temp.splice(state.indexOf(action.payload), 1);
return [action.payload, ...temp];
}
},
});
export const { updateHistory, deleteHistory, replaceHistory } = searchSlice.actions;
export default searchSlice.reducer;
// Input.jsx
import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux';
import { deleteHistory, replaceHistory, updateHistory } from '../reducer/searchSlice';
// search history ๋ณด์ฌ์ค ์ต๋ ๊ฐ์
const historyMax = 5;
export default function Input() {
const history = useSelector(state => state.searchSlice);
const dispatch = useDispatch();
const [value, setValue] = useState("");
const onChange = (e) => {
setValue(e.target.value);
}
const onEnterDown = (e) => {
if (e.key === "Enter") {
searchFunc();
}
}
function searchFunc() {
if (history.includes(value)) {
dispatch(replaceHistory(value));
} else {
if (history.length === historyMax) {
// action์ผ๋ก ์ํ ์ฒ๋ฆฌ
dispatch(deleteHistory());
dispatch(updateHistory(value));
} else {
dispatch(updateHistory(value));
}
}
}
return (
<div>
<input onChange={onChange} onKeyDown={onEnterDown}/>
<div> {history.map((value, i) => (<p key={i}>{i} : {value}</p>))}</div>
</div>
)
}
์๊ทธ์ ๋ฉํ ๋ง์์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ณด๋ค react์ ๋ ์ด์ ์ ๋์๊ณ ํ๋๋ฐ
๊ฐ์๊ธฐ ๋ฏธ๋ค์จ์ด๊น์ง ์ถ๊ฐํ๋ ค๋๊น ๋ด๊ฐ ์ง๊ธ ๋ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ง์คํ๊ณ ์๋ ๋ฏํ ๊ทธ ๊ผด์ด๋ค.
์ฝ๊ฐ ๊ณ ๋ฏผ์ด ๋๊ธด ํ์ง๋ง ... ๊ทธ๋๋ ๋ช์๊ฐ ์ ๋ ๋ ํฌ์ํ๋ ๊ฒ์ด๋ ์ผ๋จ ํ๋๋๊น์ง ํด๋ณด๊ธฐ.