어느 값에 버튼을 눌렀을 때, 입력란을 띄우는 기능을 구현해보자.
<form>
<button type ="button">수정완료</button>
</form>
위에서 button태그의 속성으로 type ="button"를 적어준 이유는 button태그는 기본적으로 일반 버튼으로 동작하지만 폼(<form>)태그 안에 있는 버튼은 기본 동작이 제출(type="submit"이 기본값)이다. 그래서 클릭 시 폼이 제출되고 페이지가 새로고침된다. 이를 방지하기 위해
type="submit": 폼 데이터를 제출합니다. (기본값)type="button": 아무런 기본 동작이 없으며, 단순히 버튼 역할만 수행합니다.type="reset": 폼 안의 입력 필드를 초기화합니다.세개의 속성 중 하나인 type="button"를 적어둔 것이다. 만약 이 버튼 태그가 폼 태그로 감싸져있지 않다면 작성하지 않아도 된다.
위 코드의 기본적인 원리는 이렇다.
<input />는 사용자로부터 데이터를 입력받기 위한 대화형 컨트롤을 나타낸다. 이 태그는 여러가지 속성 값을 통해 입력 필드의 동작을 세부적으로 조정할 수 있다.
자세한 내용과 속성들은 아래 내용에 잘 정리되어 있다.
HTML <input /> 태그 – 올바른 이해와 사용 방법
https://codingeverybody.kr/html-input-%ED%83%9C%EA%B7%B8-%EC%98%AC%EB%B0%94%EB%A5%B8-%EC%9D%B4%ED%95%B4%EC%99%80-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95/
이제 수정 코드를 활용하여 기록한 숫자들을 수정할 수 있도록 숫자 기록 코드에 적용해보자.
위 코드는 맨 위에서 작성했던 것처럼 if문으로 return값 자체를 조절하는 방식이고
위 코드는 HTML을 담은 변수 editView와 readView를 만들어 삼항연산자로 바인딩하는 방식이다.
{ editModeStatus ? editView : readView }
이제 입력란의 숫자가 변경될 수 있도록 코드를 개선해보자.
<input />의 onChange 이벤트를 사용해야 하는데, onChange 이벤트는 입력필드의 값을 변경할때 발생하는 이벤트이다.
기본적으로
<input type="text" onChange={handleInputChange} />
1. 사용하고 싶은 함수명을 바로 적어줘도 되고,
2. e 매개변수를 <input />안에서 바로 사용도 가능하다.
<input
type="text"
name="username"
value={username}
onChange={(event) => setUsername(event.target.value)}
required
/>
이런식으로 말이다.
이를 기반으로 NumberRecorderListItem컴포넌트를 작성해보면 아래와 같다.
const NumberRecorderListItem = ({ index, number, removeRecord }) => {
/* NumberRecorderList컴포넌트에게서 map()함수에서 사용된 index와 number, APP컴포넌트에서 사용된
removeRecord(숫자를 삭제하는 함수)를 받아온다. */
const [inputNumberValue, setInputNumberValue] = useState(number);
/* <input />안의 숫자 상태를 보여주는 변수이다. 기본값은 우리가 기존 저장해두었던 각각 number이다. */
const [editModeStatus, setEditModeStatus] = useState(false);
/* 이 state는 상태에 따라 수정화면(editView)과 그냥화면을(readView)을 보여주는 용도이다. */
const readView = (
<>
<button onClick={() => setEditModeStatus(true)}>수정</button>
{/*수정버튼을 클릭하면 editModeStatus를 true로 바꾸어 editView를 바인딩하도록 한다.*/}
</>
);
const editView = (
<>
<input
type="number"
placeholder="숫자를 입력해주세요"
min="1"
value={inputNumberValue}
onChange={(e) => setInputNumberValue(e.target.value)}
/>
{/*
type="number" ----> 숫자를 입력받는다.
placeholder="숫자를 입력해주세요" ----> 값이 설정되지 않앗을 때,
"숫자를 입력해주세요" 라는 문구가 나온다.
min="1" ----> 컨트롤의 최솟값은 1이다.
value={inputNumberValue} ----> 컨트롤의 초깃값은 inputNumberValue로 한다.
하지만 inputNumberValue의 초기값이 number이므로 사실상 컨트롤의 초깃값은 number이다.
onChange={(e) => setInputNumberValue(e.target.value)}
----> setInputNumberValue함수로 inputNumberValue의 값을 현재 value값으로 바꾼다.
*/}
<button onClick={() => setEditModeStatus(false)}></button>
{/*수정완료버튼을 클릭하면 editModeStatus를 false로 바꾸어 readView를 바인딩하도록 한다.*/}
<button onClick={() => setEditModeStatus(false)}>수정취소</button>
{/*수정취소버튼을 클릭하면 editModeStatus를 false로 바꾸어 readView를 바인딩하도록 한다.*/}
</>
);
return (
<li>
<span>
{index + 1}번 숫자 : {number}
</span>
<button onClick={() => removeRecord(index)}>삭제</button>
{editModeStatus ? editView : readView}
</li>
);
};
하지만 위의 코드는 입력란 안의 숫자가 변경되는 것일뿐 그 변경된 값으로 기록된 숫자가 수정되지는 않는 반쪽짜리 숫자이다.
이제 수정 완료를 누르면 기록된 숫자를 변경하는 함수를 실행하도록 해보자.
<button onClick={modifyNumber}>수정완료</button>
const modifyNumber = () => {
if (number == inputNumberValue) return;
/* 만약 기존 기록된 숫자인 number의 값과 입력란의 변경된 숫자인
inputNumberValue의 값이 같다면 아무것도 실행하지 않는다.*/
/* 만약 두 값이 다르다면 */
setNumbers(numbers.map((_number,_index) => _index == index ? inputNumberValue : _number));
/*setNumbers로 numbers의 배열을 수정하는데, map()함수를 이용해 새 배열을 생성한다.
만약, _index == index ---> 지금 map()함수의 인덱스를 나타내는 _index와
NumberRecorderList컴포넌트에서 map()으로 넘겨주었던 이 아이템의 index가
같다면
기존 element(_number)의 값을 => inputNumberValue로 만들고
다르다면
기존 element(_number)의 값을 => _number그대로 둔다.
한마디로 하면 함수가 실행되면
numbers.map()함수를 실행해 넘어온 index값으로 고칠 배열의 인덱스를 찾아낸후
그 인덱스와 일치하는 요소를 inputNumberValue, 현재 입력란에 나타나있는 값
으로 변경한 배열을 다시 setNumbers()함수를 이용해 numbers의 배열로 바꿔주는 것이다.
*/
setEditModeStatus(false);
/* editModeStatus의 값을 false로 만들어 readView가 return되도록 한다.*/
};
마지막으로 입력란의 숫자를 변경했는데 수정취소를 누른경우 다시 수정버튼을 눌렀을 때 입력란의 버튼이 초기화되도록 수정해보자.