Step 1. input 하나의 state로 관리
// 컴포넌트 return 위
const [inputValues, setInputValues] = useState({
text: '',
time: 0,
});
const inputHandler = ({ target: { name, value } }) => {
setInputValues(prev => ({ ...prev, [name]: value }));
};
// 컴포넌트 return 아래
return (
<div>
<h1>Tasks 추가</h1>
<span>text: </span>
<input
type='text'
name='text'
value={inputValues.text}
onChange={inputHandler}
/>
<span> number: </span>
<input type='number' name='time' value={inputValues.time} readOnly />
<span> start :</span>
<button>start </button>
<span> added task : </span>
<button>stop & added</button>
</div>
);
Step2. start, stop & added 버튼 함수 추가
const [timer, setTimer] = useState(null);
const startTimer = () => {
if (!timer) {
setTimer(
setInterval(() => {
setInputValues(prev => ({ ...prev, time: prev.time + 1 }));
}, 1000)
);
}
};
const addedTask = () => {
clearInterval(timer);
const data = { ...inputValues, id: createdId() };
dispatch(pushTask(data));
setInputValues({ text: '', time: 0 });
setTimer(null);
};
Step 1~2. Edit컴포넌트 완성
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { pushTask } from './redux/tasksSlice';
export default function Edit() {
const dispatch = useDispatch(); // 아래의 설명 있습니다.
const [inputValues, setInputValues] = useState({
text: '',
time: 0,
});
const [timer, setTimer] = useState(null);
const inputHandler = ({ target: { name, value } }) => {
setInputValues(prev => ({ ...prev, [name]: value }));
};
const startTimer = () => {
if (!timer) {
setTimer(
setInterval(() => {
setInputValues(prev => ({ ...prev, time: prev.time + 1 }));
}, 1000)
);
}
};
const addedTask = () => {
clearInterval(timer);
const data = { ...inputValues, id: createdId() };
dispatch(pushTask(data));
setInputValues({ text: '', time: 0 });
setTimer(null);
};
return (
<div>
<h1>Tasks 추가</h1>
<span>text: </span>
<input
type='text'
name='text'
value={inputValues.text}
onChange={inputHandler}
/>
<span> number: </span>
<input type='number' name='time' value={inputValues.time} readOnly />
<span> start :</span>
<button onClick={startTimer}>start </button>
<span> added task : </span>
<button onClick={addedTask}>stop & added</button>
</div>
);
}
let unique = 0;
function createdId() {
unique++;
return unique;
}
Step 3. Redux
& npm install react-redux @reduxjs/toolkit
참고로 redux는 패키지를 설치 안해도 된다.
import { configureStore } from '@reduxjs/toolkit';
import tasksSlice from './tasksSlice'; // 아래서 만들 함수
const store = configureStore({
reducer: {
tasks: tasksSlice,
},
});
export default store;
현재 저는 index.js에 Provider로 감쌌는데, 프로젝트의 관심사에 따라 app.js 혹은 Router.js에도 감싸도 된다.
store생성 및 Provider로 감싸기가 Redux(전역으로 값을 핸들링하기위한) 기본 세팅이다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import App from './App';
import store from './redux/store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
import { createSlice } from '@reduxjs/toolkit';
const tasksSlice = createSlice({
name: 'tasks', // store의 reducer의 key값과 동일하게
initialState: {
value: [], // 'value: ' 까지 꼭 있어야함
},
reducers: { // 여기 아래의 함수들로만 initialState를 바꿀수 있다.
pushTask: (state, action) => {
// state.value => initialState.value를 가르킴
// action.payload => 밖에서 이 함수를 실행하게되면, 값이 일로 들어온다.
state.value = [...state.value, action.payload];
},
},
});
// 함수부분을 destructurue 해서 export 한 것이다. 나중에 dispatch로 이 함수를 사용한다.
export const { pushTask } = tasksSlice.actions;
// store에서 import 한다. 상태값을 념겨준다!!
// store는 Provider가 모든 컴포넌트를 감싸면서 store를 넘겨주는데
// 그렇기 때문에 어디서든 접근할 수 있도록 store에 전달
export default tasksSlice.reducer;
import { useDispatch } from 'react-redux';
import { pushTask } from './redux/tasksSlice';
const dispatch = useDispatch(); // useDispatch를 호출하여 변수에 담기
const addedTask = () => {
clearInterval(timer);
const data = { ...inputValues, id: createdId() };
// 아래처럼 실행 인자는 1개만 넘겨 받을 수 있다.
dispatch(pushTask(data));
setInputValues({ text: '', time: 0 });
setTimer(null);
};
Step 4. redux 값 불러오기 및 Total Time 계산
import React from 'react';
import { useSelector } from 'react-redux';
export default function Tasks() {
const tasks = useSelector(({ tasks }) => tasks.value);
// === useSelector((state) => state.tasks.value)
let totalTime = 0;
for (let i = 0; i < tasks.length; i++) {
totalTime += tasks[i].time;
}
......
Step 5. 데이터 압축하기
글만 읽어봤을떄는 쉬워 보였는데, 막상 해당 문제는 해결하지 못했다.
먼저 filter로 배열을 줄이고, 같은 text value값을 가진 time value는 하나로 합친다.
코딩테스트가 끝나고 해당 알고리즘을 구현하는데만 3시간을 더 사용하였다..
function combineTasks(tasks) {
// name: sumTime
let combine = {};
for (let i = 0; i < tasks.length; i++) {
if (combine[tasks[i].text]) {
combine = {
...combine,
[tasks[i].text]: combine[tasks[i].text] + tasks[i].time,
};
}
if (!combine[tasks[i].text]) {
combine = {
...combine,
[tasks[i].text]: tasks[i].time,
};
}
}
// Array 필터 time 값은 위에서 저장하였기때문에 filter에만 집중한다.
const combineArray = tasks.filter(
(task, index, callback) =>
index === callback.findIndex(t => t.text === task.text)
);
// combine의 키값을 확인하여 일치하는 키값에 time 바꿔주기
let newArray = [...combineArray];
for (const key in combine) {
newArray = [...newArray].map(el => {
if (el.text === key) {
return { ...el, time: combine[key] };
}
return el;
});
}
return newArray;
}
Step 4~5 TasksList 컴포넌트 완성
import React from 'react';
import { useSelector } from 'react-redux';
export default function Tasks() {
const tasks = useSelector(({ tasks }) => tasks.value);
let totalTime = 0;
for (let i = 0; i < tasks.length; i++) {
totalTime += tasks[i].time;
}
const combineArray = combineTasks(tasks);
return (
<div>
<h1>Tasks List</h1>
<h2>공통 압축한 리스트 </h2>
<h3>Total Time : {totalTime} </h3>
{combineArray.map(({ id, text, time }) => (
<ul key={id} style={{ width: '200px', border: '1px solid black' }}>
<li className=''>
<h3>id: {id}</h3>
<h3>text: {text}</h3>
<h3>time: {time}</h3>
</li>
</ul>
))}
</div>
);
}
function combineTasks(tasks) {
let combine = {};
for (let i = 0; i < tasks.length; i++) {
if (combine[tasks[i].text]) {
combine = {
...combine,
[tasks[i].text]: combine[tasks[i].text] + tasks[i].time,
};
}
if (!combine[tasks[i].text]) {
combine = {
...combine,
[tasks[i].text]: tasks[i].time,
};
}
}
const combineArray = tasks.filter(
(task, index, callback) =>
index === callback.findIndex(t => t.text === task.text)
);
let newArray = [...combineArray];
for (const key in combine) {
newArray = [...newArray].map(el => {
if (el.text === key) {
return { ...el, time: combine[key] };
}
return el;
});
}
return newArray;
}
첫 코테 후기