10장은 실습이라 따라하는 과정을 전부 기록하는 것 보단 그 과정에서 깨닫게된 점 내가 잘못 알고 있던 점들만 빼내어서 기록하려고 한다
그렇게 하기 위해선 우선 책에서 ~~~한 기능을 구현할 것이라고 소개하면 내가 먼저 그러한 기능을 만들어보고 난 뒤에 내 코드와 책의 코드를 비교하면서 진행했다
10-1. props 전달하기
아래는 책 10-3의 기능구현하기 부분에서 props를 전달하는 과정이다
const App = () => {
const [todos, setTodos] = useState([
{ id:1,text : '리액트의 기초 알아보기',checked: true},
{ id:2,text : '컴포넌트 스타일링 해보기',checked: true},
{ id:3,text : '일정 관리 앱 만들어 보기',checked: false},
]);
return (
<TodoTemplate>
<TodoInsert/>
<TodoList todos={todos} exp={ex1} ex2={ex2}/>
</TodoTemplate>
)
};
위와 같이 id, text, checked 객체를 가진 todos 배열을 TodoList에게 전달해줬고
// in TodoList.js
const TodoList = (todos)=>{
const forRender = todos.map(todo=><TodoListItem key={todo.id} todo={todo}/>);
return(
<div className="Todolist">
{forRender}
</div>
);
}
이렇게 props를 받아서 데이터에 접근하려고 했으나..
어째서인지 todos.map은 함수가 아니라는 에러를 뱉어대고 있었다
그래서 todos를 콘솔로 찍어봤는데 배열이 넘어가는 것이 아니라 객체형태로 wrapping되어서 넘어간다는 걸 다시 한번깨달았고
const forRender = todos.todos.map(todo=><TodoListItem key={todo.id} todo={todo}/>);
이런식으로 접근하니까 제대로 데이터에 접근이 가능했지만 코드가 매우 더러워서 보기 너무 싫었다..
해결
해결법은 매우 간단했는데 구조분해식으로 props값을 받아오면 wrapping된 객체를 벗기고 간편하게 접근하는게 가능하다
// in TodoList.js
const TodoList = ({todos})=>{
const forRender = todos.map(todo=><TodoListItem key={todo.id} todo={todo}/>);
return(
<div className="Todolist">
{forRender}
</div>
);
}
여러개의 props?
너무 당연할 수도 있지만 나는 코드를 짜다가 막혀서 남겨둔다..
return (
<TodoTemplate>
<TodoInsert/>
<TodoList todos={todos} exp={exp} ex2={ex2}/>
</TodoTemplate>
)
이처럼 여러개의 props를 넘겨준다면 어떻게 받아야할까?
// in TodoList.js
const TodoList = ({todos}, {exp}, {ex2})=>{
...
}
이렇게 받아서 에러를 뱉었다.. props로 넘어오는 것들을 하나의 객체로 묶어 받는다 라고 생각했어야 했는데 다르게 생각하고 있었다..
// in TodoList.js
const TodoList = ({todos, exp, ex2})=>{
...
}
이렇게 받는게 맞다
10-2. classnames 라이브러리
다음은 classnames 라이브러리 사용에 관해선데
이 전장인 스타일링 파트를 내가 제대로 듣지 않아서 그런지 제대로 사용하지 못했다
const TodoListItem = ({todo})=>{
const {text, checked} = todo;
return(
<div className='TodoListItem'>
**<div className={cn('checkbox',checked?'checked':'')}>**
{checked ? <MdCheckBox/> : <MdCheckBoxOutlineBlank/>}
<div className="text">{text}</div>
</div>
<div className="remove">
<MdRemoveCircleOutline/>
</div>
</div>
);
}
저런식으로 cheked가 트루면 checked 클래스를 추가 아니면 없게 코드를 짰었는데
const TodoListItem = ({todo})=>{
const {text, checked} = todo;
return(
<div className='TodoListItem'>
**<div className={cn('checkbox', {checked})}>**
{checked ? <MdCheckBox/> : <MdCheckBoxOutlineBlank/>}
<div className="text">{text}</div>
</div>
<div className="remove">
<MdRemoveCircleOutline/>
</div>
</div>
);
}
내 생각에는 {}안에는 js코드가 들어가고 checked는 true값이니까 true가 클래스 네임으로 들어갈 것 같은데
html 태그를 확인해 보면 "checkbox checked"가 클래스 네임이다
10-3. 추가 기능 구현하기
추가 기능을 구현하는 단계였는데 시작부터 막막했다
const TodoInsert = ({onInsert})=>{
const [value, setValue] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
};
const handleChange = (e) => {
setValue(e.target.value);
};
return(
<form className="TodoInsert" onSubmit={handleSubmit}>
<input type="text" name={'todoText'} value={value} onChange={handleChange}/>
<button type={"submit"}>
<MdAdd/>
</button>
</form>
);
}
input의 value를 useState로 관리하는 것까진 알겠는데 이 value를 어떻게 상위 컴포넌트로 보내서 todos 배열에 추가시킬까 고민을 계속했다
리액트의 데이터는 상위에서 하위 컴포넌트로 흐른다 는 말을 들어본 적이있는데 아래의 코드를 보고 그 말이 다시 한번 떠올랐다
데이터를 바꿔서 위로 올려줄 순 없기 때문에 데이터를 바꿔주는 함수를 밑으로 내려서 위의 데이터를 수정하는 방식의 코드이다
const App = () => {
const [todos, setTodos] = useState([
{ id:1,text : '리액트의 기초 알아보기',checked: true},
{ id:2,text : '컴포넌트 스타일링 해보기',checked: true},
{ id:3,text : '일정 관리 앱 만들어 보기',checked: false},
]);
const nextId = useRef(todos.length+1);
const onInsert = useCallback(
text=>{
const todo = {
id:nextId.current,
text,
checked:false
};
setTodos(todos.concat(todo));
nextId.current++;
},
[todos]
);
return (
<TodoTemplate>
<TodoInsert onInsert={onInsert}/>
<TodoList todos={todos}/>
</TodoTemplate>
)
};
onIsert함수는 text값을 받아서 todo 객체를 만들고 todos에 추가시켜주는 함수이고 그 함수를 다시 TodoInsert에 props로 보낸다
const TodoInsert = ({onInsert})=>{
const [value, setValue] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
onInsert(value);
setValue('');
};
...
}
받아온 onInsert를 사용해 주는 모습
nextId?
nextId는 렌더링되는 데이터가 아니기 때문에 ref로 선언해주고 사용하고 있다
useCallback
본문에서는 useCallback을 마구마구 사용해주고 있는데 나는 아직 어떤 때에 구분해서 써야하는지 잘 모르겠다..
조금 더 많은 코드를 짜봐야 최적화를 이해할 수 있을 듯 하다
'React > 리액트를 다루는 기술' 카테고리의 다른 글
12. immer (0) | 2021.02.05 |
---|---|
11. 컴포넌트 성능 최적화 (0) | 2021.02.01 |
9. 컴포넌트 스타일링 (0) | 2021.01.25 |
8. Hooks (0) | 2021.01.22 |
7. 라이프사이클 메서드 (0) | 2021.01.20 |