상태 유지 문제
함수는 일시적으로 메모리를 할당받아서 사용하다가 수행이 종료되면 소멸
인스턴스는 정리 하지 않으면 메모리에 계속 존재하게 됨
이전에 수행한 결과를 다음 수행에 사용하고자 하는 경우
HTTP 와 HTTPS 도 연결을 계속 유지하지 않는다.
세션(서버에 저장)과 쿠키라는 개념을 이용해서 상태를 유지
로컬 스토리지 나 Web SQL, indexedDB, Web Socket 등의 개념도 사
클래스 컴포넌트는 수명 주기 관련 메서드가 존재
데이터 관리 - 상태 유지
useMemo
useCallback
useState
useReducer
수명 주기 관련
useEffect - 비동기
useLayoutEffect - 동기
메서드 호출
데이터 공유
매개변수 1개 (데이터) : useState useRef useImperativeHandle useContext
매개변수 2개 (콜백함수, 의존성 목록 - deps) : useMemo useCallback useReducer useEffect useLayoutEffect
<컴포넌트이름 이름=값 .../> 사용은 prpos.이름
<컴포넌트>childrn</컴포넌트>
props 와 state 는 변경이 되면 컴포넌트가 rerendering 된다.
생성
속성을 가져오고 수정하기 위한 메서드
getter : 속성의 값을 가져오는 메서드
get속성이름() 의 형태로 만드는데 속성 이름의 첫글자는 대문자
자료형이 bool 인 경우는 get 대신에 is 사용
최근의 언어에서는 get 대신에 속성 이름으로 바로 접근할 수 있게 만들어 준다
setter : 속성의 값을 수정하는 메서드
import React, {Component} from 'react';
// 버튼을 누르면 숫자를 1씩 증가시켜서 출력하느 컴포넌트
class ClassComponent extends Component{
constructor(props){
super(props);
this.state = {
count:0
}
}
render(){
return(
<>
<p>{this.state.count} 번 클릭</p>
<button
onClick = {(e) => {this.setState({count:this.state.count +1 })}}>
Button
</button>
</>
)
}
}
export default ClassComponent;
import logo from './logo.svg';
import './App.css';
import ClassComponent from './components/ClssComponent';
function App() {
return (
<>
<ClassComponent/>
</>
);
}
export default App;
import React from "react";
function FunctionComponent(){
let n = 0;
function handleClick(){
n = n + 1;
console.log(n);
}
return(
<>
<p>{n}</p>
<button onClick={handleClick}>Button</button>
</>
);
}
export default FunctionComponent;
import './App.css';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
function App() {
return (
<>
<ClassComponent/>
<FunctionComponent/>
</>
);
}
export default App;
import React from "react";
function FunctionComponent(){
//일반 변수를 만들어서 사용을 하면 값이 변경되더라도 재출력되지 않음
let n = 0;
function handleClick(){
n = n + 1;
console.log(n);
//강제로 출력하는 코드 작성
document.getElementById('display').innerHTML = n;
}
return(
<>
<p id='display'>{n}</p>
<button onClick={handleClick}>Button</button>
</>
);
}
export default FunctionComponent;
import React, { useState } from "react";
function FunctionComponent(){
/*let n = 0;
function handleClick(){
n = n + 1;
console.log(n);
//강제로 출력하는 코드 작성
document.getElementById('display').innerHTML = n;*/
/*let state = {
n:0
}
function handleClick(){
state = {
n:state.n + 1
}
}*/
//state 생성
const [count, setCount] = useState(0);
function handleClick(){
setCount(count + 1);
}
return(
<>
<p id='display'>{count}</p>
<button onClick={handleClick}>Button</button>
</>
);
}
export default FunctionComponent;
import React ,{useState} from "react"; //useState 에 해당하는 에만 여기{}에 넣어 놓고 시작하겠다
const Info = () => {
const [name, setName] = useState(''); //초기값
const [lee, setLee] = useState('');
const onChangeName = (e) => {
setName(e.target.value);
}
const onChangeLee = (e) => {
setLee(e.target.value);
}
return(
<>
<div>
<input name='name' value={name} onChange={onChangeName}/>
<input name='lee' value={lee} onChange={onChangeLee}/>
</div>
<div>
<b>이름:{name}</b>
</div>
<div>
별명:{lee}
</div>
</>
)
}
export default Info;
import './App.css';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
function App() {
return (
<>
<ClassComponent/>
<FunctionComponent/>
<Info/>
</>
);
}
export default App;
개요
state 가 변경된 후 수행할 side effect 효과를 설정하는 hook
이 함수에서는 state를 사용할 수 있음
Class Component 의
componentDidMount(컴포넌트가 출력되고 난 후 호출되는 메서드), componentDidUpdate(컴포넌트가 업데이트 되고 난 후 호출되는 메서드), componentWillUnmount(컴포넌트가 화면에서 제거된 후 호출되는 메서드)를 합친 메서드
Class Component 에서의 수명주기 메서드 확인
Not Strict Mode 로 실행 - index.js 의 strict 태그 지움
수명 주기 확인을 위한 컴포넌트 작성 ClassEffect.jsx 파일 생성 후 작성
import React from "react";
class ClassEffect extends React.Component{
state = {
count: 0
}
componentDidMount(){
console.log('컴포넌트가 화면에 출력된 후 호출되는 메서드');
// 탭에 출력되는 문자열 설정 - 검색 엔진이 기본적으로 title을 가지고 검색
document.title = `${this.state.count} 번 클릭`;
}
componentDidUpdate(){
console.log('컴포넌트가 업데이트 된 후 호출');
document.title = `${this.state.count} 번 클릭`;
}
render(){
return(
<>
<p>{this.state.count}</p>
<button onClick={(e)=> {this.setState({count:this.state.count + 1})}}>
Click
</button>
</>
)
}
}
export default ClassEffect;
import './App.css';
import ClassEffect from './components/ClassEffect';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
function App() {
return (
<>
<Info/>
<ClassComponent/>
<FunctionComponent/>
<ClassEffect/>
</>
);
}
export default App;
Info.jsx 파일 수정
import React ,{useState,useEffect} from "react"; //useState 에 해당하는 에만 여기{}에 넣어 놓고 시작하겠다
const Info = () => {
const [name, setName] = useState(''); //초기값
const [lee, setLee] = useState('');
useEffect(()=>{
console.log('콜백함수');
})
const onChangeName = (e) => {
setName(e.target.value);
}
const onChangeLee = (e) => {
setLee(e.target.value);
}
return(
<>
<div>
<input name='name' value={name} onChange={onChangeName}/>
<input name='lee' value={lee} onChange={onChangeLee}/>
</div>
<div>
<b>이름:{name}</b>
</div>
<div>
별명:{lee}
</div>
</>
)
}
export default Info;
import React ,{useState,useEffect} from "react"; //useState 에 해당하는 에만 여기{}에 넣어 놓고 시작하겠다
const Info = () => {
const [name, setName] = useState(''); //초기값
const [lee, setLee] = useState('');
// 두번째 매개변수를 생성략하면 state가 변경될 때 마다 호출
//두번째 매개변수에 []를 대입하면 Mount 될때만 호출
useEffect(()=>{
console.log('콜백함수');
},[])
const onChangeName = (e) => {
setName(e.target.value);
}
const onChangeLee = (e) => {
setLee(e.target.value);
}
return(
<>
<div>
<input name='name' value={name} onChange={onChangeName}/>
<input name='lee' value={lee} onChange={onChangeLee}/>
</div>
<div>
<b>이름:{name}</b>
</div>
<div>
별명:{lee}
</div>
</>
)
}
export default Info;
import React ,{useState,useEffect} from "react"; //useState 에 해당하는 에만 여기{}에 넣어 놓고 시작하겠다
const Info = () => {
const [name, setName] = useState(''); //초기값
const [lee, setLee] = useState('');
// 두번째 매개변수를 생성략하면 state가 변경될 때 마다 호출
//두번째 매개변수에 []를 대입하면 Mount 될때만 호출
//두번째 매개변수에 state를 나열하면 나열된 state 값이 변경될 때 호출된다.
useEffect(()=>{
console.log('콜백함수');
},[name])
const onChangeName = (e) => {
setName(e.target.value);
}
const onChangeLee = (e) => {
setLee(e.target.value);
}
return(
<>
<div>
<input name='name' value={name} onChange={onChangeName}/>
<input name='lee' value={lee} onChange={onChangeLee}/>
</div>
<div>
<b>이름:{name}</b>
</div>
<div>
별명:{lee}
</div>
</>
)
}
export default Info;
import './App.css';
import ClassEffect from './components/ClassEffect';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
import React, {useState} from 'react';
function App() {
const [visible, setVisible] = useState(false);
return (
<>
<button onClick={()=> {setVisible(!visible)}}>
{visible ? '숨기기' : '보이기'}
</button>
<hr/>
{visible && <Info/>}
<hr/>
<Info/>
<ClassComponent/>
<FunctionComponent/>
<ClassEffect/>
</>
);
}
export default App;
import './App.css';
import ClassEffect from './components/ClassEffect';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
import React, {useState} from 'react';
function App() {
const [visible, setVisible] = useState(false);
return (
<>
<button onClick={()=> {setVisible(!visible)}}>
{visible ? '숨기기' : '보이기'}
</button>
<hr/>
{visible && <Info/>}
<hr/>
{!visible && <ClassEffect/>}
<Info/>
<ClassComponent/>
<FunctionComponent/>
<ClassEffect/>
</>
);
}
export default App;
사용방법은 useEffect 와 동일
useEffect 는 비동기적으로 수행되고 useLayoutEffect는 동기적으로 수행
공식문서에는 useEffect 사용을 권장
개요
ref는 변수
데이터를 보관할 수 있고 컴포넌트를 가리키는 것이 가능
상태를 유지해야 하는데 화면을 재출력 할 필요가 없는 경우에 state 대신에 ref를 사용
일반적인 자바스크립트는 DOM 객체를 가져올때 id를 설정한 후 document.getElementById()를 이용 하거나 document.querySelector(선택자)를 이용하는데 리액트 에서는 ref사용을 권장
생성
사용
클래스 형 컴포넌트에서는 변수를 사용하고자 할 때는 일반적인 변수 선언을 이용해서 사용
Info.jsx 를 수정해서 버튼을 추가한 후 버튼을 누르면 name 과 lee를 초기화 하고 name에 포커스(state를 사용할 수 없음)를 설정
import React ,{useState, useEffect, useRef} from "react"; //useState 에 해당하는 에만 여기{}에 넣어 놓고 시작하겠다
const Info = () => {
const [name, setName] = useState(''); //초기값
const [lee, setLee] = useState('');
// 두번째 매개변수를 생성략하면 state가 변경될 때 마다 호출
//두번째 매개변수에 []를 대입하면 Mount 될때만 호출
//두번째 매개변수에 state를 나열하면 나열된 state 값이 변경될 때 호출된다.
useEffect(()=>{
console.log('이 함수는 컴포넌트가 화면에서 사라질 때 호출됩니다');
},[name])
const onChangeName = (e) => {
setName(e.target.value);
}
const onChangeLee = (e) => {
setLee(e.target.value);
}
//변수 생성
const nameInput = useRef();
//버튼을 클릭했을 때 호출되는 함수
const handleClick = () => {
setName('');
setLee(''); //초기화 완료
nameInput.current.focus();
}
return(
<>
<div>
<input name='name' value={name} onChange={onChangeName}
ref = {nameInput}/>
<input name='lee' value={lee} onChange={onChangeLee}/>
<button onClick={handleClick}>초기화 버튼</button>
</div>
<div>
<b>이름:{name}</b>
</div>
<div>
별명:{lee}
</div>
</>
)
}
export default Info;
데이터를 추가할 수 있는 화면 과 목록 화면(여러 컴포넌트가 필요함)을 구성
데이터는 App.js가 소유
App.js 와 index.js 는 Entry Point
모든 컴포넌트는 App.js에서 출력이 되어야 한다
여러 컴포넌트에서 사용할 데이터가 필요하다면 App.js에 만드는 것
* 시작하자마자 필요한 데이터가 있다면 App.js에 만드는 것이 좋다
목록을 출력할 컴포넌트를 생성 - UserList.jsx
import React from "react";
function CreateUser({username, email, onChange, onCreate}) {
return(
<div>
<input placeholder="이름을 입력"
name='username'
value={username}
onChange={onChange}/>
<input placeholder="이메일을 입력"
name = 'email'
value={email}
onChange={onChange}/>
<button onClick={onCreate}>등록</button>
</div>
)
}
export default CreateUser
import './App.css';
import ClassEffect from './components/ClassEffect';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
import React, {useState} from 'react';
import UserList from './components/UserList';
function App() {
const [visible, setVisible] = useState(false);
// 기본 데이터 생성
const [users,setUsers] = useState([
{
id:1,
username:'leeeuijoo',
email:'leeeuijoo@naver.com'
},
{
id:2,
username:'leejoo',
email:'leejoo@gmail.com'
},
{
id:3,
username:'euijoo',
email:'euijoo@hanmail.net'
}
])
return (
<>
<UserList users={users} />
<button onClick={()=> {setVisible(!visible)}}>
{visible ? '숨기기' : '보이기'}
</button>
<hr/>
{visible && <Info/>}
<hr/>
{!visible && <ClassEffect/>}
<Info/>
<ClassComponent/>
<FunctionComponent/>
<ClassEffect/>
</>
);
}
export default App;
mport React from "react";
function CreateUser({username, email, onChange, onCreate}) {
return(
<div>
<input placeholder="이름을 입력"
name='username'
value={username}
onChange={onChange}/>
<input placeholder="이메일을 입력"
name = 'email'
value={email}
onChange={onChange}/>
<button onClick={onCreate}>등록</button>
</div>
)
}
export default CreateUser
import './App.css';
import ClassEffect from './components/ClassEffect';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
import React, {useState,useRef} from 'react';
import UserList from './components/UserList';
import CreateUser from './components/CreateUser';
function App() {
const [visible, setVisible] = useState(false);
// 삽입에 필요한 state 생성
const [inputs, setInputs] = useState({
username:'',
email:''
});
//입력 상자의 내용이 변경될 때 호출되는 함수
const {username, email} = inputs;
const onChange = (e) => {
//객체의 특정 속성의 값만 수정해서 리턴하고자 하면
//...inputs 하게되면 inputs 의 모든 내용을 복제해서 새로운 객체를 생성함
// name 속성에 해당하는 것만 value 로 수정 - > 굉장히 많이 사용함 불변성 떄문
//리액트에서는 ...을 많이 사용한다
//리액트의 props 나 state는 직접 수정이 불가하다
// 객체나 배열을 복사해서 작업하기 때문에 많이 사용
const {name,value} = e.target;
setInputs({
...inputs,
[name]: value
})
}
//등록 처리
const nextId = useRef(4);
const onCreate = () => {
// 추가할 데이터 생성
const user={
id:nextId.current,
username,
email
}
//users를 복제한 것과 user를 하나의 배열로 생성
setUsers([...users, user])
//state 는 직접 수정이 안되므로 일단 복제
let temp = [...users];
//email을 기준으로 오름차순 정렬
temp.sort((a,b)=>{
if (a.email > b.email) return 1
else if (a.email === b.email) return 0
else return -1
});
// 정렬한 결과를 state 에 반영
setUsers(temp);
//입력 도구 초기화
setInputs({
username:'',
email:''
})
nextId.current = nextId.current + 1;
}
// 기본 데이터 생성
const [users,setUsers] = useState([
{
id:1,
username:'leeeuijoo',
email:'leeeuijoo@naver.com'
},
{
id:2,
username:'leejoo',
email:'leejoo@gmail.com'
},
{
id:3,
username:'euijoo',
email:'euijoo@hanmail.net'
}
])
return (
<>
<CreateUser username={username} email={email}
onChange={onChange} onCreate={onCreate}/>
<UserList users={users} />
<button onClick={()=> {setVisible(!visible)}}>
{visible ? '숨기기' : '보이기'}
</button>
<hr/>
{visible && <Info/>}
<hr/>
{!visible && <ClassEffect/>}
<Info/>
<ClassComponent/>
<FunctionComponent/>
<ClassEffect/>
</>
);
}
export default App;
import React from 'react';
function User({ user, onRemove }) {
return (
<>
<p><b>{user.username}</b> <span>({user.email})</span>
<button onClick={()=> onRemove(user.id)}>삭제</button>
</p>
</>
);
}
function UserList({ users, onRemove}) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove} />
))}
</div>
);
}
export default UserList;
import './App.css';
import ClassEffect from './components/ClassEffect';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
import React, {useState,useRef} from 'react';
import UserList from './components/UserList';
import CreateUser from './components/CreateUser';
function App() {
const [visible, setVisible] = useState(false);
// 삽입에 필요한 state 생성
const [inputs, setInputs] = useState({
username:'',
email:''
});
//입력 상자의 내용이 변경될 때 호출되는 함수
const {username, email} = inputs;
const onChange = (e) => {
//객체의 특정 속성의 값만 수정해서 리턴하고자 하면
//...inputs 하게되면 inputs 의 모든 내용을 복제해서 새로운 객체를 생성함
// name 속성에 해당하는 것만 value 로 수정 - > 굉장히 많이 사용함 불변성 떄문
//리액트에서는 ...을 많이 사용한다
//리액트의 props 나 state는 직접 수정이 불가하다
// 객체나 배열을 복사해서 작업하기 때문에 많이 사용
const {name,value} = e.target;
setInputs({
...inputs,
[name]: value
})
}
//등록 처리
const nextId = useRef(4);
const onCreate = () => {
// 추가할 데이터 생성
const user={
id:nextId.current,
username,
email
}
//users를 복제한 것과 user를 하나의 배열로 생성
setUsers([...users, user])
//state 는 직접 수정이 안되므로 일단 복제
let temp = [...users];
//email을 기준으로 오름차순 정렬
temp.sort((a,b)=>{
if (a.email > b.email) return 1
else if (a.email === b.email) return 0
else return -1
});
// 정렬한 결과를 state 에 반영
setUsers(temp);
//입력 도구 초기화
setInputs({
username:'',
email:''
})
nextId.current = nextId.current + 1;
}
// 기본 데이터 생성
const [users,setUsers] = useState([
{
id:1,
username:'leeeuijoo',
email:'leeeuijoo@naver.com'
},
{
id:2,
username:'leejoo',
email:'leejoo@gmail.com'
},
{
id:3,
username:'euijoo',
email:'euijoo@hanmail.net'
}
])
//배열에서 전달된 데이터에 해당하는 User 객체를 삭제하기
// id 가 아닌것만 골라냄
const onRemove = (id) => {
setUsers(users.filter((user)=>{return user.id !== id}));
}
return (
<>
<CreateUser username={username} email={email}
onChange={onChange} onCreate={onCreate}/>
<UserList users={users} onRemove={onRemove}/>
<button onClick={()=> {setVisible(!visible)}}>
{visible ? '숨기기' : '보이기'}
</button>
<hr/>
{visible && <Info/>}
<hr/>
{!visible && <ClassEffect/>}
<Info/>
<ClassComponent/>
<FunctionComponent/>
<ClassEffect/>
</>
);
}
export default App;
import './App.css';
import ClassEffect from './components/ClassEffect';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
import React, {useState,useRef} from 'react';
import UserList from './components/UserList';
import CreateUser from './components/CreateUser';
function App() {
const [visible, setVisible] = useState(false);
// 삽입에 필요한 state 생성
const [inputs, setInputs] = useState({
username:'',
email:''
});
//입력 상자의 내용이 변경될 때 호출되는 함수
const {username, email} = inputs;
const onChange = (e) => {
//객체의 특정 속성의 값만 수정해서 리턴하고자 하면
//...inputs 하게되면 inputs 의 모든 내용을 복제해서 새로운 객체를 생성함
// name 속성에 해당하는 것만 value 로 수정 - > 굉장히 많이 사용함 불변성 떄문
//리액트에서는 ...을 많이 사용한다
//리액트의 props 나 state는 직접 수정이 불가하다
// 객체나 배열을 복사해서 작업하기 때문에 많이 사용
const {name,value} = e.target;
setInputs({
...inputs,
[name]: value
})
}
//등록 처리
const nextId = useRef(4);
const onCreate = () => {
// 추가할 데이터 생성
const user={
id:nextId.current,
username,
email
}
//users를 복제한 것과 user를 하나의 배열로 생성
setUsers([...users, user])
//state 는 직접 수정이 안되므로 일단 복제
let temp = [...users];
//email을 기준으로 오름차순 정렬
temp.sort((a,b)=>{
if (a.email > b.email) return 1
else if (a.email === b.email) return 0
else return -1
});
// 정렬한 결과를 state 에 반영
setUsers(temp);
//입력 도구 초기화
setInputs({
username:'',
email:''
})
nextId.current = nextId.current + 1;
}
// 기본 데이터 생성
// 속성 추가
const [users,setUsers] = useState([
{
id:1,
username:'leeeuijoo',
email:'leeeuijoo@naver.com',
active:true
},
{
id:2,
username:'leejoo',
email:'leejoo@gmail.com',
active:true
},
{
id:3,
username:'euijoo',
email:'euijoo@hanmail.net',
active:false
}
])
//전달된 id에 해당하는 데이터의 active 를 반전시키는 함수
const onToggle = (id) =>{
setUsers(
users.map((user)=>{return user.id === id ? {...user, active:!user.active}: user})
)
}
//배열에서 전달된 데이터에 해당하는 User 객체를 삭제하기
// id 가 아닌것만 골라냄
const onRemove = (id) => {
setUsers(users.filter((user)=>{return user.id !== id}));
}
return (
<>
<CreateUser username={username} email={email}
onChange={onChange} onCreate={onCreate}/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
<button onClick={()=> {setVisible(!visible)}}>
{visible ? '숨기기' : '보이기'}
</button>
<hr/>
{visible && <Info/>}
<hr/>
{!visible && <ClassEffect/>}
<Info/>
<ClassComponent/>
<FunctionComponent/>
<ClassEffect/>
</>
);
}
export default App;
import './App.css';
import ClassEffect from './components/ClassEffect';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
import React, {useState,useRef} from 'react';
import UserList from './components/UserList';
import CreateUser from './components/CreateUser';
function App() {
const [visible, setVisible] = useState(false);
// 삽입에 필요한 state 생성
const [inputs, setInputs] = useState({
username:'',
email:''
});
//입력 상자의 내용이 변경될 때 호출되는 함수
const {username, email} = inputs;
const onChange = (e) => {
//객체의 특정 속성의 값만 수정해서 리턴하고자 하면
//...inputs 하게되면 inputs 의 모든 내용을 복제해서 새로운 객체를 생성함
// name 속성에 해당하는 것만 value 로 수정 - > 굉장히 많이 사용함 불변성 떄문
//리액트에서는 ...을 많이 사용한다
//리액트의 props 나 state는 직접 수정이 불가하다
// 객체나 배열을 복사해서 작업하기 때문에 많이 사용
const {name,value} = e.target;
setInputs({
...inputs,
[name]: value
})
}
//등록 처리
const nextId = useRef(4);
const onCreate = () => {
// 추가할 데이터 생성
const user={
id:nextId.current,
username,
email
}
//users를 복제한 것과 user를 하나의 배열로 생성
setUsers([...users, user])
//state 는 직접 수정이 안되므로 일단 복제
let temp = [...users];
//email을 기준으로 오름차순 정렬
temp.sort((a,b)=>{
if (a.email > b.email) return 1
else if (a.email === b.email) return 0
else return -1
});
// 정렬한 결과를 state 에 반영
setUsers(temp);
//입력 도구 초기화
setInputs({
username:'',
email:''
})
nextId.current = nextId.current + 1;
}
// 기본 데이터 생성
// 속성 추가
const [users,setUsers] = useState([
{
id:1,
username:'leeeuijoo',
email:'leeeuijoo@naver.com',
active:true
},
{
id:2,
username:'leejoo',
email:'leejoo@gmail.com',
active:true
},
{
id:3,
username:'euijoo',
email:'euijoo@hanmail.net',
active:false
}
])
//전달된 id에 해당하는 데이터의 active 를 반전시키는 함수
const onToggle = (id) =>{
setUsers(
users.map((user)=>{return user.id === id ? {...user, active:!user.active}: user})
)
}
//배열에서 전달된 데이터에 해당하는 User 객체를 삭제하기
// id 가 아닌것만 골라냄
const onRemove = (id) => {
setUsers(users.filter((user)=>{return user.id !== id}));
}
return (
<>
<CreateUser username={username} email={email}
onChange={onChange} onCreate={onCreate}/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
<button onClick={()=> {setVisible(!visible)}}>
{visible ? '숨기기' : '보이기'}
</button>
<hr/>
{visible && <Info/>}
<hr/>
{!visible && <ClassEffect/>}
<Info/>
<ClassComponent/>
<FunctionComponent/>
<ClassEffect/>
</>
);
}
export default App;
import React from 'react';
function User({ user, onRemove ,onToggle}) {
return (
<>
<p><b onClick={() => onToggle(user.id)} style={{cursor:'pointer', color:user.active ? 'green':'black'}}>
{user.username}:</b></p>
<p><span>({user.email})</span>
<button onClick={()=> onRemove(user.id)}>삭제</button>
</p>
</>
);
}
function UserList({ users, onRemove, onToggle}) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove}
onToggle = {onToggle} />
))}
</div>
);
}
export default UserList;
import React, {useEffect} from 'react';
function User({ user, onRemove ,onToggle}) {
useEffect(()=> {
console.log('컴포넌트가 화면에 나타남');
return ()=>{
console.log('컴포넌트가 화면에서 사라짐');
}
})
return (
<>
<p><b onClick={() => onToggle(user.id)} style={{cursor:'pointer', color:user.active ? 'green':'black'}}>
{user.username}:</b></p>
<p><span>({user.email})</span>
<button onClick={()=> onRemove(user.id)}>삭제</button>
</p>
</>
);
}
function UserList({ users, onRemove, onToggle}) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove}
onToggle = {onToggle} />
))}
</div>
);
}
export default UserList;
import './App.css';
import ClassEffect from './components/ClassEffect';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
import React, {useState,useRef} from 'react';
import UserList from './components/UserList';
import CreateUser from './components/CreateUser';
//active 속성이 true 인 데이터의 개수 출력하는 함수
function countActiveUsers(users) {
console.log('활성 사용자 수를 세는중...');
return users.filter(user => user.active).length;
}
function App() {
const [visible, setVisible] = useState(false);
// 삽입에 필요한 state 생성
const [inputs, setInputs] = useState({
username:'',
email:''
});
//입력 상자의 내용이 변경될 때 호출되는 함수
const {username, email} = inputs;
const onChange = (e) => {
//객체의 특정 속성의 값만 수정해서 리턴하고자 하면
//...inputs 하게되면 inputs 의 모든 내용을 복제해서 새로운 객체를 생성함
// name 속성에 해당하는 것만 value 로 수정 - > 굉장히 많이 사용함 불변성 떄문
//리액트에서는 ...을 많이 사용한다
//리액트의 props 나 state는 직접 수정이 불가하다
// 객체나 배열을 복사해서 작업하기 때문에 많이 사용
const {name,value} = e.target;
setInputs({
...inputs,
[name]: value
})
}
//등록 처리
const nextId = useRef(4);
const onCreate = () => {
// 추가할 데이터 생성
const user={
id:nextId.current,
username,
email
}
//users를 복제한 것과 user를 하나의 배열로 생성
setUsers([...users, user])
//state 는 직접 수정이 안되므로 일단 복제
let temp = [...users];
//email을 기준으로 오름차순 정렬
temp.sort((a,b)=>{
if (a.email > b.email) return 1
else if (a.email === b.email) return 0
else return -1
});
// 정렬한 결과를 state 에 반영
setUsers(temp);
//입력 도구 초기화
setInputs({
username:'',
email:''
})
nextId.current = nextId.current + 1;
}
// 기본 데이터 생성
// 속성 추가
const [users,setUsers] = useState([
{
id:1,
username:'leeeuijoo',
email:'leeeuijoo@naver.com',
active:true
},
{
id:2,
username:'leejoo',
email:'leejoo@gmail.com',
active:true
},
{
id:3,
username:'euijoo',
email:'euijoo@hanmail.net',
active:false
}
])
//전달된 id에 해당하는 데이터의 active 를 반전시키는 함수
const onToggle = (id) =>{
setUsers(
users.map((user)=>{return user.id === id ? {...user, active:!user.active}: user})
)
}
//배열에서 전달된 데이터에 해당하는 User 객체를 삭제하기
// id 가 아닌것만 골라냄
const onRemove = (id) => {
setUsers(users.filter((user)=>{return user.id !== id}));
}
//active 가 true 인 데이터 수 세기
//함수를 호출하면 무조건 함수가 수행
const count = countActiveUsers(users);
return (
<>
<CreateUser username={username} email={email}
onChange={onChange} onCreate={onCreate}/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
<div>활성사용자 수 : {count}</div>
<button onClick={()=> {setVisible(!visible)}}>
{visible ? '숨기기' : '보이기'}
</button>
<hr/>
{visible && <Info/>}
<hr/>
{!visible && <ClassEffect/>}
<Info/>
<ClassComponent/>
<FunctionComponent/>
<ClassEffect/>
</>
);
}
export default App;
import './App.css';
import ClassEffect from './components/ClassEffect';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
import React, {useState,useRef, useMemo} from 'react';
import UserList from './components/UserList';
import CreateUser from './components/CreateUser';
//active 속성이 true 인 데이터의 개수 출력하는 함수
function countActiveUsers(users) {
console.log('활성 사용자 수를 세는중...');
return users.filter(user => user.active).length;
}
function App() {
const [visible, setVisible] = useState(false);
// 삽입에 필요한 state 생성
const [inputs, setInputs] = useState({
username:'',
email:''
});
//입력 상자의 내용이 변경될 때 호출되는 함수
const {username, email} = inputs;
const onChange = (e) => {
//객체의 특정 속성의 값만 수정해서 리턴하고자 하면
//...inputs 하게되면 inputs 의 모든 내용을 복제해서 새로운 객체를 생성함
// name 속성에 해당하는 것만 value 로 수정 - > 굉장히 많이 사용함 불변성 떄문
//리액트에서는 ...을 많이 사용한다
//리액트의 props 나 state는 직접 수정이 불가하다
// 객체나 배열을 복사해서 작업하기 때문에 많이 사용
const {name,value} = e.target;
setInputs({
...inputs,
[name]: value
})
}
//등록 처리
const nextId = useRef(4);
const onCreate = () => {
// 추가할 데이터 생성
const user={
id:nextId.current,
username,
email
}
//users를 복제한 것과 user를 하나의 배열로 생성
setUsers([...users, user])
//state 는 직접 수정이 안되므로 일단 복제
let temp = [...users];
//email을 기준으로 오름차순 정렬
temp.sort((a,b)=>{
if (a.email > b.email) return 1
else if (a.email === b.email) return 0
else return -1
});
// 정렬한 결과를 state 에 반영
setUsers(temp);
//입력 도구 초기화
setInputs({
username:'',
email:''
})
nextId.current = nextId.current + 1;
}
// 기본 데이터 생성
// 속성 추가
const [users,setUsers] = useState([
{
id:1,
username:'leeeuijoo',
email:'leeeuijoo@naver.com',
active:true
},
{
id:2,
username:'leejoo',
email:'leejoo@gmail.com',
active:true
},
{
id:3,
username:'euijoo',
email:'euijoo@hanmail.net',
active:false
}
])
//전달된 id에 해당하는 데이터의 active 를 반전시키는 함수
const onToggle = (id) =>{
setUsers(
users.map((user)=>{return user.id === id ? {...user, active:!user.active}: user})
)
}
//배열에서 전달된 데이터에 해당하는 User 객체를 삭제하기
// id 가 아닌것만 골라냄
const onRemove = (id) => {
setUsers(users.filter((user)=>{return user.id !== id}));
}
//active 가 true 인 데이터 수 세기
//함수를 호출하면 무조건 함수가 수행
//useMemo 를 이용해서 함수를 호출하면 두 번째 매개변수의 값이 변경된 경우에만
//함수를 호출해서 연산을 다시 수행한다.
const count = useMemo(()=>countActiveUsers(users), [users]);
return (
<>
<CreateUser username={username} email={email}
onChange={onChange} onCreate={onCreate}/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
<div>활성사용자 수 : {count}</div>
<button onClick={()=> {setVisible(!visible)}}>
{visible ? '숨기기' : '보이기'}
</button>
<hr/>
{visible && <Info/>}
<hr/>
{!visible && <ClassEffect/>}
<Info/>
<ClassComponent/>
<FunctionComponent/>
<ClassEffect/>
</>
);
}
export default App;
import React, {useState} from "react";
//배열을 매개변수로 받아서 배열의 평균을 리턴해주는 함수 구현
const getAverage = (numbers) => {
console.log('평균 게산 중입니다');
if(numbers.length === 0){
return 0;
}
//배열의 데이터 합계
//reduce 는 배열을 순회하면서 연산을 한 결과를 누적시켜 하나의 값으로 리턴
//첫번째 매개변수는 누적되는 데이터이고 두번째는 배열의 데이터
const sum = numbers.reduce((a,b)=> {return a+b;})
return sum / numbers.length; //나눠줘야죠!
}
const Average = () => {
const [list,setList] = useState([]);
const [number, setNumber] = useState('');
const onChange = (e) => {setNumber(e.target.value)}
const onInsert = (e) => {
//number를 정수로 변환시켜 list 와 결합 -> nextList 에 대입
const nextList = list.concat(parseInt(number));
//nexList 를 list로 설정
setList(nextList);
setNumber('');
}
return (
<div>
<input value={number} onChange={onChange}/>
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index)=> (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균:</b>{getAverage(list)}
</div>
</div>
)
}
export default Average;
import Average from './components/Average';
// 리턴 안에
<Average/> //추가
❓ 평균을 언제 계산할지를 잘 생각해야 한다
Average.jsx 파일을 수정해서 데이터에 변화가 발생한 경우에만 평균을 다시계산
import React, {useState, useMemo} from "react";
//배열을 매개변수로 받아서 배열의 평균을 리턴해주는 함수 구현
const getAverage = (numbers) => {
console.log('평균 게산 중입니다');
if(numbers.length === 0){
return 0;
}
//배열의 데이터 합계
//reduce 는 배열을 순회하면서 연산을 한 결과를 누적시켜 하나의 값으로 리턴
//첫번째 매개변수는 누적되는 데이터이고 두번째는 배열의 데이터
const sum = numbers.reduce((a,b)=> {return a+b;})
return sum / numbers.length; //나눠줘야죠!
}
const Average = () => {
const [list,setList] = useState([]);
const [number, setNumber] = useState('');
const onChange = (e) => {setNumber(e.target.value)}
const onInsert = (e) => {
//number를 정수로 변환시켜 list 와 결합 -> nextList 에 대입
const nextList = list.concat(parseInt(number));
//nexList 를 list로 설정
setList(nextList);
setNumber('');
}
//list에 변화가 생길 때에만
const avg = useMemo(()=> {return getAverage(list)}, [list]);
return (
<div>
<input value={number} onChange={onChange}/>
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index)=> (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균:</b>{avg}
</div>
</div>
)
}
export default Average;
import React, {useState, useMemo,useCallback} from "react";
//배열을 매개변수로 받아서 배열의 평균을 리턴해주는 함수 구현
const getAverage = (numbers) => {
console.log('평균 게산 중입니다');
if(numbers.length === 0){
return 0;
}
//배열의 데이터 합계
//reduce 는 배열을 순회하면서 연산을 한 결과를 누적시켜 하나의 값으로 리턴
//첫번째 매개변수는 누적되는 데이터이고 두번째는 배열의 데이터
const sum = numbers.reduce((a,b)=> {return a+b;})
return sum / numbers.length; //나눠줘야죠!
}
const Average = () => {
const [list,setList] = useState([]);
const [number, setNumber] = useState('');
//함수가 맨 처음 만들어지고 다시는 새로 생성되지 않도록 설정
const onChange = useCallback( (e) => {setNumber(e.target.value)},[]);
//list 와 number 라는 state 를 사용하기 때문에
// 처음 한 번만 함수를 생성하라고 하면 경고가 발생함
//list 와 number에 변화가 발생하면 함수를 다시 생성
const onInsert = useCallback((e) => {
//number를 정수로 변환시켜 list 와 결합 -> nextList 에 대입
const nextList = list.concat(parseInt(number));
//nexList 를 list로 설정
setList(nextList);
setNumber('');
},[list,number])
//list에 변화가 생길 때에만
const avg = useMemo(()=> {return getAverage(list)}, [list]);
return (
<div>
<input value={number} onChange={onChange}/>
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index)=> (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균:</b>{avg}
</div>
</div>
)
}
export default Average;
import './App.css';
import ClassEffect from './components/ClassEffect';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
import React, {useState,useRef, useMemo, useCallback} from 'react';
import UserList from './components/UserList';
import CreateUser from './components/CreateUser';
import Average from './components/Average';
//active 속성이 true 인 데이터의 개수 출력하는 함수
function countActiveUsers(users) {
console.log('활성 사용자 수를 세는중...');
return users.filter(user => user.active).length;
}
function App() {
const [visible, setVisible] = useState(false);
// 삽입에 필요한 state 생성
const [inputs, setInputs] = useState({
username:'',
email:''
});
//입력 상자의 내용이 변경될 때 호출되는 함수
const {username, email} = inputs;
const onChange = useCallback((e) => { //useCallback 작용
//객체의 특정 속성의 값만 수정해서 리턴하고자 하면
//...inputs 하게되면 inputs 의 모든 내용을 복제해서 새로운 객체를 생성함
// name 속성에 해당하는 것만 value 로 수정 - > 굉장히 많이 사용함 불변성 떄문
//리액트에서는 ...을 많이 사용한다
//리액트의 props 나 state는 직접 수정이 불가하다
// 객체나 배열을 복사해서 작업하기 때문에 많이 사용
const {name,value} = e.target;
setInputs({
...inputs,
[name]: value
})
}, [inputs])
//등록 처리
const nextId = useRef(4); //useCallback 사용
// 기본 데이터 생성
// 속성 추가
const [users,setUsers] = useState([
{
id:1,
username:'leeeuijoo',
email:'leeeuijoo@naver.com',
active:true
},
{
id:2,
username:'leejoo',
email:'leejoo@gmail.com',
active:true
},
{
id:3,
username:'euijoo',
email:'euijoo@hanmail.net',
active:false
}
])
const onCreate = useCallback( ()=> {
// 추가할 데이터 생성
const user={
id:nextId.current,
username,
email
}
//users를 복제한 것과 user를 하나의 배열로 생성
setUsers([...users, user])
//state 는 직접 수정이 안되므로 일단 복제
let temp = [...users];
//email을 기준으로 오름차순 정렬
temp.sort((a,b)=>{
if (a.email > b.email) return 1
else if (a.email === b.email) return 0
else return -1
});
// 정렬한 결과를 state 에 반영
setUsers(temp);
//입력 도구 초기화
setInputs({
username:'',
email:''
})
nextId.current = nextId.current + 1;
},[email, username, users])
//전달된 id에 해당하는 데이터의 active 를 반전시키는 함수
const onToggle = useCallback((id) =>{
setUsers(
users.map((user)=>{return user.id === id ? {...user, active:!user.active}: user})
)
},[users]);
//배열에서 전달된 데이터에 해당하는 User 객체를 삭제하기
// id 가 아닌것만 골라냄
const onRemove = useCallback((id) => {
setUsers(users.filter((user)=>{return user.id !== id}));
},[users]);
//active 가 true 인 데이터 수 세기
//함수를 호출하면 무조건 함수가 수행
//useMemo 를 이용해서 함수를 호출하면 두 번째 매개변수의 값이 변경된 경우에만
//함수를 호출해서 연산을 다시 수행한다.
const count = useMemo(()=>countActiveUsers(users), [users]);
return (
<>
<CreateUser username={username} email={email}
onChange={onChange} onCreate={onCreate}/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
<div>활성사용자 수 : {count}</div>
<button onClick={()=> {setVisible(!visible)}}>
{visible ? '숨기기' : '보이기'}
</button>
<hr/>
{visible && <Info/>}
<hr/>
{!visible && <ClassEffect/>}
<Info/>
<ClassComponent/>
<FunctionComponent/>
<ClassEffect/>
<Average/>
</>
);
}
export default App;
개요
export 부분을 감싸주면 된다.
CreateUser.jsx
export default React.memo(CreateUser);
export default React.memo(UserList);
개요
useState 보다 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트하고자 할 때 사용하는 Hook
useState 를 사용하게 되면 컴포넌트 내부에 state 를 업데이트하는 로직을 작성해야 함
컴포넌트의 역할은 화면 출력인데 컴포넌트 안에 처리 로직이 포함됨
reducer 는 상태 업데이트 로직을 컴포넌트 외부 혹은 다른 파일에 작성해서 사용할 수 있도록 해준다
reducer 를 만들 때는 현재 상테 그리고 업데이트를 위해서 필요한 정보를 담은 액션을 전달 받아서 새로운 상태를 반환하도록 해야 하며 불변성은 지켜야 한다
형태
function reducer(state, action){
상태 변화
}
const [state,dispatch]= useReducer(reducer, 초기 상태);
## 필요할 때 dispatch({type : 동작을 구분하기 위한 상수})
import React, {useState} from "react";
function Counter(){
//데이터 생성
const [number, setNumber] = useState(0);
const onIncrease = () => {
setNumber(preNumber => preNumber + 1);
}
const onDecrease = () => {
setNumber(preNumber => preNumber -1 );
}
// 이런식으로 로직을 컴포넌트에 넣지 말라는 얘기
return(
<>
<h2>{number}</h2>
<button onClick={onIncrease}>플러스 1</button>
<button onClick={onDecrease}>마이너스 1</button>
</>
);
}
export default React.memo(Counter); //리액트 코드를 작성하면 왠만하면 memo 사용할 것
import './App.css';
import ClassEffect from './components/ClassEffect';
import ClassComponent from './components/ClssComponent';
import FunctionComponent from './components/FunctionComponent';
import Info from './components/Info';
import React, {useState,useRef, useMemo, useCallback} from 'react';
import UserList from './components/UserList';
import CreateUser from './components/CreateUser';
import Average from './components/Average';
import Counter from './components/Counter';
//active 속성이 true 인 데이터의 개수 출력하는 함수
function countActiveUsers(users) {
console.log('활성 사용자 수를 세는중...');
return users.filter(user => user.active).length;
}
function App() {
const [visible, setVisible] = useState(false);
// 삽입에 필요한 state 생성
const [inputs, setInputs] = useState({
username:'',
email:''
});
//입력 상자의 내용이 변경될 때 호출되는 함수
const {username, email} = inputs;
const onChange = useCallback((e) => { //useCallback 작용
//객체의 특정 속성의 값만 수정해서 리턴하고자 하면
//...inputs 하게되면 inputs 의 모든 내용을 복제해서 새로운 객체를 생성함
// name 속성에 해당하는 것만 value 로 수정 - > 굉장히 많이 사용함 불변성 떄문
//리액트에서는 ...을 많이 사용한다
//리액트의 props 나 state는 직접 수정이 불가하다
// 객체나 배열을 복사해서 작업하기 때문에 많이 사용
const {name,value} = e.target;
setInputs({
...inputs,
[name]: value
})
}, [inputs])
//등록 처리
const nextId = useRef(4); //useCallback 사용
// 기본 데이터 생성
// 속성 추가
const [users,setUsers] = useState([
{
id:1,
username:'leeeuijoo',
email:'leeeuijoo@naver.com',
active:true
},
{
id:2,
username:'leejoo',
email:'leejoo@gmail.com',
active:true
},
{
id:3,
username:'euijoo',
email:'euijoo@hanmail.net',
active:false
}
])
const onCreate = useCallback( ()=> {
// 추가할 데이터 생성
const user={
id:nextId.current,
username,
email
}
//users를 복제한 것과 user를 하나의 배열로 생성
setUsers([...users, user])
//state 는 직접 수정이 안되므로 일단 복제
let temp = [...users];
//email을 기준으로 오름차순 정렬
temp.sort((a,b)=>{
if (a.email > b.email) return 1
else if (a.email === b.email) return 0
else return -1
});
// 정렬한 결과를 state 에 반영
setUsers(temp);
//입력 도구 초기화
setInputs({
username:'',
email:''
})
nextId.current = nextId.current + 1;
},[email, username, users])
//전달된 id에 해당하는 데이터의 active 를 반전시키는 함수
const onToggle = useCallback((id) =>{
setUsers(
users.map((user)=>{return user.id === id ? {...user, active:!user.active}: user})
)
},[users]);
//배열에서 전달된 데이터에 해당하는 User 객체를 삭제하기
// id 가 아닌것만 골라냄
const onRemove = useCallback((id) => {
setUsers(users.filter((user)=>{return user.id !== id}));
},[users]);
//active 가 true 인 데이터 수 세기
//함수를 호출하면 무조건 함수가 수행
//useMemo 를 이용해서 함수를 호출하면 두 번째 매개변수의 값이 변경된 경우에만
//함수를 호출해서 연산을 다시 수행한다.
const count = useMemo(()=>countActiveUsers(users), [users]);
return (
<>
<Counter />
<CreateUser username={username} email={email}
onChange={onChange} onCreate={onCreate}/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
<div>활성사용자 수 : {count}</div>
<button onClick={()=> {setVisible(!visible)}}>
{visible ? '숨기기' : '보이기'}
</button>
<hr/>
{visible && <Info/>}
<hr/>
{!visible && <ClassEffect/>}
<Info/>
<ClassComponent/>
<FunctionComponent/>
<ClassEffect/>
<Average/>
</>
);
}
export default App;
import React, {useReducer} from "react"; //useState 지우고 useReducer 로 대체
function reducer(state, action){
switch(action.type){
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
function Counter(){
//데이터 생성
//reducer 함수의 state 가 number 로 설정되는 것 과 동일한 효과
//reducer 함수가 리턴하는 결과가 number 에 설정되서 state 를 수정하는 것과 동일한 효과
const [number, dispatch] = useReducer(reducer, 0); //방금 만든 함수를 가져오고 다음에 초기값을 가져온다
const onIncrease = () => {
dispatch({type:'INCREMENT'});
}
const onDecrease = () => {
dispatch({type:'DECREMENT'});
}
// 이런식으로 로직을 컴포넌트에 넣지 말라는 얘기
return(
<>
<h2>{number}</h2>
<button onClick={onIncrease}>플러스 1</button>
<button onClick={onDecrease}>마이너스 1</button>
</>
);
}
export default React.memo(Counter); //리액트 코드를 작성하면 왠만하면 memo 사용할 것
import React, { useRef, useReducer, useMemo, useCallback } from 'react';
import UserList from '/Users/euijoolee/Desktop/React/hooks/src/components/UserList';
import CreateUser from '/Users/euijoolee/Desktop/React/hooks/src/components/CreateUser';
function countActiveUsers(users) {
console.log('활성 사용자 수를 세는중...');
return users.filter(user => user.active).length;
}
const initialState = {
inputs: {
username: '',
email: ''
},
users: [
{
id:1,
username:'leeeuijoo',
email:'leeeuijoo@naver.com',
active:true
},
{
id:2,
username:'leejoo',
email:'leejoo@gmail.com',
active:true
},
{
id:3,
username:'euijoo',
email:'euijoo@hanmail.net',
active:false
}
]
};
function reducer(state, action) {
switch (action.type) {
case 'CHANGE_INPUT':
return {
...state,
inputs: {
...state.inputs,
[action.name]: action.value
}
};
case 'CREATE_USER':
return {
inputs: initialState.inputs,
users: state.users.concat(action.user)
};
case 'TOGGLE_USER':
return {
...state,
users: state.users.map(user =>
user.id === action.id ? { ...user, active: !user.active } : user
)
};
case 'REMOVE_USER':
return {
...state,
users: state.users.filter(user => user.id !== action.id)
};
default:
return state;
}
}
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
const nextId = useRef(4);
const { users } = state;
const { username, email } = state.inputs;
const onChange = useCallback(e => {
const { name, value } = e.target;
dispatch({
type: 'CHANGE_INPUT',
name,
value
});
}, []);
const onCreate = useCallback(() => {
dispatch({
type: 'CREATE_USER',
user: {
id: nextId.current,
username,
email
}
});
nextId.current += 1;
}, [username, email]);
const onToggle = useCallback(id => {
dispatch({
type: 'TOGGLE_USER',
id
});
}, []);
const onRemove = useCallback(id => {
dispatch({
type: 'REMOVE_USER',
id
});
}, []);
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onToggle={onToggle} onRemove={onRemove} />
<div>활성사용자 수 : {count}</div>
</>
);
}
export default App;