현재 이런 상태이고, input에 값을 입력하고 버튼을 누르면
[{name: "미키"}, {name: "새로입력한 값"}, .......]
이런 배열을 만들고 싶다.
현재코드는 이렇다.
import React, { useState } from "react";
import "./App.css";
import react from "react";
function App() {
const [todo, setTodo] = useState([{ name: "미키" }]);
let [inputValue, setInputValue] = useState("");
const inputChg = (e) => {
setInputValue(e.target.value);
};
const onSubmit = (e) => {
e.preventDefault();
};
console.log(todo);
return (
<div className="parent">
<p>id: {todo[0].id}</p>
<p>name: {todo[0].name}</p>
<p>memo: {todo[0].memo}</p>
<form onSubmit={onSubmit}>
<input value={inputValue} onChange={inputChg}></input>
<button>저장</button>
</form>
</div>
);
}
export default App;
(1) ...array (스프레드 연산자) 사용
원래 array (그러니까 oldArray)를 ...를 이용해 복사하고, 그 뒤에 element를 추가한다. 이렇게 복사한 array에 element를 추가한 값으로 setTheArray를 바꿔준다.
const [theArray, setTheArray] = useState(initialArray);
setTheArray(oldArray => [...oldArray, newElement]);
oldArray에는 마음대로 작명하면 되고, 원래 만들어놓은 배열이라고 잘 이해한다.
setTheArray([...theArray, newElement]);
동일한 결과가 나온다.
방법1이랑 2가 과정에 차이가 있다는데..
음 그러면 저 inputValue를 객체로 만들어놓으면 되겠다! 그러면 [{ }, { }, { }]이렇게 계속 추가할 수 있을 것 같다.
let newInfo = {
id: "n",
todo: todoValue,
memo: memoValue,
};
이렇게 데이터를 작성했어서 오류가 나지 않았나보다. 그렇지만 리액트에서는 원본 데이터를 건드리지 않아야 하니까 저렇게 지속적으로 데이터를 바꾸는 방식은 좋지 않을 것 같아 수정했다.
이번엔 아래와 같이 useState를 이용하였더니 마지막 글자가 안보이는 에러가 생겼다.
let [newTodo, setNewTodo] = useState({ name: inputValue });
전체코드
import React, { useEffect, useState } from "react";
import "./App.css";
import react from "react";
function App() {
let [inputValue, setInputValue] = useState("");
const [todo, setTodo] = useState([{ name: "" }]);
let [newTodo, setNewTodo] = useState({ name: inputValue });
const inputChg = (e) => {
setInputValue(e.target.value);
setNewTodo({ name: inputValue });
};
const OnSubmit = (e) => {
e.preventDefault();
setTodo([...todo, newTodo]);
};
const todosMap = todo.map((todoItem, i) => <p key={i}>{todoItem.name}</p>);
return (
<div className="parent">
name: {todosMap}
<form onSubmit={OnSubmit}>
<input value={inputValue} onChange={inputChg}></input>
<button>저장</button>
</form>
</div>
);
}
export default App;
클래스 컴포넌트에서 setState() function은 비동기적으로 작동해서 그렇다는데.. 함수형도 그런건가?? https://stackoverflow.com/questions/33088482/onchange-in-react-doesnt-capture-the-last-character-of-text
찾아보니 useEffect hook을 이용해서 input값을 온전하게 업데이트 할 수 있다고 한다. 그렇게 해보자.
앞에서 말했다시피 useEffect는 마운트(첫번째로 나타날)될 때도 실행되고, 이후에 렌더링할 때마다도 실행된다. 그런데 만약 컴포넌트가 첫번째로 나타날 때만 쓰고 싶다고 하면 아래와 같이 사용하면 된다.
useEffect(렌더링 후 실행할 함수, [deps]);
//deps?
//특정한 값이 변경될 때 effect함수를 실행하고 싶을 경우 배열 안에 그 값을 넣어준다. [name]이렇게 쓰면 name변수가 변경될 때 함수가 실행된다.
//컴포넌트가 Mount될 때에만 실행하고 싶으면 빈 배열을 쓰면 된다.
import React, { useEffect, useState } from "react";
import "./App.css";
import react from "react";
function App() {
const [todo, setTodo] = useState([{ name: "" }]);
let [inputValue, setInputValue] = useState("");
let [newTodo, setNewTodo] = useState({ name: "" });
const inputChg = (e) => {
setInputValue(e.target.value);
setNewTodo({ name: inputValue });
};
const onSubmit = (e) => {
e.preventDefault();
setTodo([...todo, newTodo]);
setInputValue("");
};
const todosMap = todo.map((todoItem, i) => <p key={i}>{todoItem.name}</p>);
return (
<div className="parent">
name: {todosMap}
<form onSubmit={onSubmit}>
<input value={inputValue} onChange={inputChg}></input>
<button>저장</button>
</form>
</div>
);
}
export default App;
콘솔로그를 해보면 아무 동작을 하지않았음에도 처음에 렌더링이 두번 되어있다. 알아서 useEffect가 실행된듯하다ㅠ 그래서 로드되자마자 지 혼자 인풋 값이 비어있는 상태에서 바로 배열에 추가해버림..
[deps]추가했으면 이거 수정할 때만 되는거 아닌가? 그게 아니라, [deps]가 수정될 때 뿐만 아니라 처음 렌더링될때에도 실행된다.
이번에는 deps 에 특정 값을 넣어보도록 하겠습니다. deps 에 특정 값을 넣게 된다면, 컴포넌트가 처음 마운트 될 때에도 호출이 되고, 지정한 값이 바뀔 때에도 호출이 됩니다. 그리고, deps 안에 특정 값이 있다면 언마운트시에도 호출이되고, 값이 바뀌기 직전에도 호출이 됩니다. 출처: https://react.vlpt.us/basic/16-useEffect.html
코드는 이렇게 작성했었다
import React, { useEffect, useState } from "react";
import "./App.css";
import react from "react";
function App() {
const [todo, setTodo] = useState([{ name: "" }]);
let [inputValue, setInputValue] = useState("");
let [newTodo, setNewTodo] = useState({ name: "" });
const inputChg = (e) => {
setInputValue(e.target.value);
};
const onSubmit = (e) => {
e.preventDefault();
setNewTodo({ name: inputValue });
};
useEffect(() => setTodo([...todo, newTodo]), [newTodo]);
console.log(todo);
const todosMap = todo.map((todoItem, i) => <p key={i}>{todoItem.name}</p>);
return (
<div className="parent">
name: {todosMap}
<form onSubmit={onSubmit}>
<input value={inputValue} onChange={inputChg}></input>
<button>저장</button>
</form>
</div>
);
}
export default App;
newTodo가 바뀔 때마다 setTodo를 이용해서, 전체 todo의 값에 새로운 todo를 추가해주려고 했다.
그런데 오류가 나서 구조를 수정하였다.
1) input값을 useState("")에 넣는다.
아직 단순한 string 상태이다.
2) 아까 input값으로 만든 useState를 이용해 객체를 만든다.
이제 객체를 만들었다.
useEffect를 사용해서, input값이 변할 때마다 객체를 그 최신 input값으로 업데이트 해준다.
//inputValue가 변하면, 그때 newTodo값을 바꿔주자
useEffect(() => setNewTodo({ name: inputValue }), [inputValue]);
{key: 인풋값} 이런식이다.
3) 해당 객체를 배열에 추가한다.
const onSubmit = (e) => {
...
setTodo([...todo, newTodo]);
...
};
input값이 변할 때, 그 때 newTodo값을 바꿔주기로 하였다.
완성 코드
import React, { useEffect, useState } from "react";
import "./App.css";
import react from "react";
function App() {
const [todo, setTodo] = useState([{ name: "" }]);
let [inputValue, setInputValue] = useState("");
let [newTodo, setNewTodo] = useState({ name: "" });
const inputChg = (e) => {
setInputValue(e.target.value);
};
//inputValue가 변하면, 그때 newTodo값을 바꿔주자
useEffect(() => setNewTodo({ name: inputValue }), [inputValue]);
const onSubmit = (e) => {
e.preventDefault();
setTodo([...todo, newTodo]);
setInputValue("");
};
const todosMap = todo.map((todoItem, i) => <p key={i}>{todoItem.name}</p>);
return (
<div className="parent">
name: {todosMap}
<form onSubmit={onSubmit}>
<input value={inputValue} onChange={inputChg}></input>
<button>저장</button>
</form>
</div>
);
}
export default App;
(참고:
https://thewebdev.info/2021/05/29/how-to-capture-the-last-character-of-text-entered-with-onchange-in-react/ ,
https://tocomo.tistory.com/32
)