2월 25일 여정 48일차이다.
오늘은 토이 프로잭트하면서 어려웠던 부분에 대해서 기록을 남길려고 한다.
기능을 하나 구현해야 되는데 난이도가 높았다. 주된 기능은 버튼을 누르면 select와 Input이 추가되야 했다. 구글의 도움을 받아 만들어진 코드를 리뷰해보고자 한다.
const [fields, setFields] = useState([
{ id: 1, selectValue: "1", inputValue: "" },
]);
function addField() {
if (fields.length >= MAX_FIELDS) {
return;
}
export function createNewFields(fields) {
const maxSelectValue = getMaxSelectValue(fields);
const newFields = [...fields];
newFields.push({
id: fields.length + 1,
selectValue: String(maxSelectValue + 1),
inputValue: "",
});
return newFields;
}
function getMaxSelectValue(fields) {
return Math.max(fields.map((field) => Number(field.selectValue)));
}
위에 2개는 새로운 form 만들기 버튼을 눌렀을때 동작하는 코드이다. 일단 addField 함수를 보면 fields는 배열로 안에 객체가 있다. 그것의 길이가 내가 정해놓은 MAX_FIELDS보다 클 경우 추가를 못하게 막아 놓았다.
createNewFields전에 getMaxSelectValue를 먼저 보게되면 여기서는 fields는 배열 안에 객체로되어 있다. 그것을 map을 돌려 field.selectValue의 최대값을 리턴하였다.
이후에 createNewFields에는 getMaxSelectValue에서 리턴한 최대값과 그리고 newFiedls안에 원소를 복사하여 새로운 것을 만들고 있다. 즉 새로 추가하기 버튼을 누르면 fields에 객체의 형태로 id와 그리고 selectValue는 이전의 selectValue +1 그리고 inpu에는 아무것도 안써져있는 select와 input이 배열 안에 들어가면서 만들어진다.
export function handleSelectChange(fields, id, event) {
const newFields = [...fields];
const index = newFields.findIndex((field) => field.id === id);
if (index >= 0) {
newFields[index] = {
...newFields[index],
selectValue: event.target.value,
};
return newFields;
}
return fields;
}
여기는 select의 값을 바꿀때이다. 기본적으로 fields를 복사하여 넣어놓는다. 계속 복사하는 이유는 바로 리액트의 불변성 때문이다. 배열과 객체는 불변성(리랜더링)을 위하여 항상 복사한다. 복사하면서 기존의 배열과는 다른 참조를 갖게된다. 이후에 내가 지금 선택하고 있는 select의 아이디를 findIndex로 찾아서 index가 0보다 클 때 즉 slect가 존재할때 밑의 내용이 실행된다. 역시나 ...newFields[index]를 복사하여 새로운 객체를 만들고 기존의 객체의 속성과 값은 모두 유지한다. 그리고 내가 선택한 새로운 value를 넣어준다. 그 값을 newFields에 반환하고 마지막으로 fields에 반환하게 된다.
export function handleInputChange(fields, id, event) {
const newFields = [...fields];
const index = newFields.findIndex((field) => field.id === id);
if (index >= 0) {
newFields[index] = {
...newFields[index],
inputValue: event.target.value,
};
return newFields;
}
return fields;
}
여기 input 역시 위의 select의 기능과 똑같다. 가장 중요한것은 리액트 불변성 유지이다. [...fields], ...newFields[index] 이렇게 2부분이 불변성을 위한 곳이다.
export function handleDelete(fields, id) {
const newFields = fields.filter((field) => field.id !== id);
return newFields;
}
여기서는 fields에서 배열의 id를 찾아서 filter로 같은 않은 id를 제외한 나머지를 반환하도록 하였다.
export function getOutput(fields) {
return fields.map((field) => ({
selectValue: field.selectValue,
inputValue: field.inputValue,
}));
}
여기서는 fields배열 안에서 값을 추출하기 위해서 map을 사용하여 value를 반환하고 있다.
굉장히 단순한 기능인데도 여러 복잡한 locgic이 있다는 것에 감탄했다.