[유데미x스나이퍼팩토리] 10주 완성 프로젝트 캠프 23일차
🍊 React
1. React 핵심개념
컴포넌트
- 기존 MVC 패턴은 각 요소간 의존성이 높아 재사용이 어려움
- React 컴포넌트는 view를 독립적으로 구성, 재활용이 용이함
컴포넌트 구성 요소
데이터 구성 요소 | 특징 |
---|---|
State | 컴포넌트의 상태를 저장하고 변경 할 수 있는 데이터 |
Props | 부모 컴포넌트에서 자식 컴포넌트로 전달되는 읽기 전용 데이터 |
Context | 부모 컴포넌트에서 생성하여 모든 자식 컴포넌트에 전달하는 데이터 |
컴포넌트 종류
컴포넌트 종류 | 특징 |
---|---|
함수형 컴포넌트 | JavaScript에서 사용하는 함수를 이용해 만든 컴포넌트 |
클래스형 컴포넌트 | ES6에서 지원하는 class 문법을 이용해 만든 컴포넌트 |
✅ 함수형 컴포넌트를 사용하도록 권장
- 함수형 컴포넌트가 가독성이 좋고 간결
- Hook의 등장으로 사용하기 편함
- render 함수가 필요 없어 마운트 속도가 빠름
// 함수형 컴포넌트 생성
function Component(props){
return <div>Hello</div>
}
function App(){
return(
<Component /> // 호출
)
}
export default App; // 다른 js파일에서 호출 할 수 있도록 내보내기
Hook
함수형 컴포넌트가 클래스형 컴포넌트 기능을 해줄 수 있게 하는 함수
State Hook 👇🏻
- useState: 동적으로 바뀌는 값 관리 (컴포넌트 내부)
- useReducer: 컴포넌트 상태 업데이트 로직을 분리하여 관리
Effect Hook 👇🏻
- useEffect: 컴포넌트가 렌더링 될 때마다 특정작업을 수행할 수 있게 하는 함수
State (useState)
- 동적으로 바뀌는 값을 관리할 때 사용, 상태 유지 값과 그 값을 갱신하는 함수를 반환
- 함수 컴포넌트 내부에서 상태 업데이트 관리
const[상태 값 저장 변수, 상태 값 갱신 함수] = useState(상태 초기값)
State (useReducer)
- 함수 컴포넌트 상태 업데이트 로직을 컴포넌트에서
분리하여 사용
- 현재
상태 객체
와행동 객체를 인자
로 받아,새로운 상태 객체
를 반환하는 Hook 함수
const[상태 객체, dispatch 함수] = useReducer(reducer함수, 초기 상태, 초기 함수)
함수명 | 함수 정의 | 설명 |
---|---|---|
reducer 함수 | reducer(state, action) | 현재 상태객체(state)와 행동 객체(action)를 인자로 받아서 새로운 state를 반환 |
dispatch 함수 | 생태 변경을 일으키기 위해 사용되는데 reducer 함수에 넘길 action 객체를 받음 |
Props
- 컴포넌트에 값을 전달하기 위해 사용
- 상위 컴포넌트에서 하위 컴포넌트로 전달되는 읽기 전용 데이터
- 객체 형태로 전달되며 props 이름은 동일해야함
function Hello(props){
return (
<div>
안녕하세요,
저는 {props.name}입니다.
좋아하는 색상은 {props.color}입니다.
</div>
)
}
Effect (useEffect)
- 리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 실행할 수 있게 하는 hook
useEffect(실행할 함수, 의존성 배열)
의존성 배열이 없을 경우 👇🏻
React.useEffect(() => {
dispatch(getActions.getFundingAC(page));
});
- 페이지가 렌더링 될 때마다 함수(dispatch)가 실행
의존성 배열이 비어있을 경우 👇🏻
React.useEffect(() => {
dispatch(getActions.getFundingAC(page));
}, []);
- 첫 렌더링 시에만 함수(dispatch)가 실행
의존성 배열에 값이 있을 경우 👇🏻
const [page, setPage] = React.useState(1)
React.useEffect(() => {
dispatch(getActions.getFundingAC(page));
}, [page]);
- 해당값(page) 값이 변할 때마다 함수(dispatch)가 실행
2. 과제 - Momentum(Ract)
App.js
import './App.css';
import React, { useState, useEffect } from 'react';
const WeatherIcons = {
'01': 'fas fa-sun',
'02': 'fas fa-cloud-sun',
'03': 'fas fa-cloud',
'04': 'fas fa-cloud-meatball',
'09': 'fas fa-cloud-sun-rain',
'10': 'fas fa-cloud-showers-heavy',
'11': 'fas fa-poo-storm',
'13': 'far fa-snowflake',
'50': 'fas fa-smog',
};
function App() {
const [weatherIcon, setWeatherIcon] = useState('');
const [temperature, setTemperature] = useState('');
const [city, setCity] = useState('');
useEffect(() => {
const API_KEY = 'd126eafe75d75dc8f3cb05542db8df4d';
const handleGeoSuccess = (pos) => {
const { latitude, longitude } = pos.coords;
fetch(
`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`
)
.then((response) => response.json())
.then((data) => {
const iconCode = data.weather[0].icon.substr(0, 2);
const temperature = Math.floor(data.main.temp) + '℃';
const city = data.name;
setWeatherIcon(iconCode);
setTemperature(temperature);
setCity(city);
})
.catch((error) => {
console.log('Error fetching weather data:', error);
});
};
const handleGeoError = () => {
console.log('Error getting geolocation.');
};
navigator.geolocation.getCurrentPosition(handleGeoSuccess, handleGeoError);
}, []);
const [time, setTime] = useState('');
useEffect(() => {
const interval = setInterval(() => {
const date = new Date();
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
setTime(`${hours}:${minutes}:${seconds}`);
}, 1000);
return () => {
clearInterval(interval);
};
}, []);
const [username, setUsername] = useState('');
const [greeting, setGreeting] = useState('');
useEffect(() => {
const savedUsername = localStorage.getItem('username');
if (savedUsername) {
setUsername(savedUsername);
}
}, []);
useEffect(() => {
const date = new Date();
const time = date.getHours();
let greetingWord = '';
if (0 <= time && time < 5) {
greetingWord = 'Good day';
} else if (5 <= time && time < 9) {
greetingWord = 'Good morning';
} else if (9 <= time && time < 17) {
greetingWord = 'Good afternoon';
} else {
greetingWord = 'Good evening';
}
setGreeting(`${greetingWord} ${username} :D`);
}, [username]);
const handleLoginSubmit = (e) => {
e.preventDefault();
const enteredUsername = e.target.elements.username.value.trim();
if (enteredUsername) {
setUsername(enteredUsername);
localStorage.setItem('username', enteredUsername);
}
};
const [todos, setTodos] = useState(() => {
const savedTodos = localStorage.getItem('todos');
if (savedTodos) {
try {
return JSON.parse(savedTodos);
} catch (error) {
console.log('Error parsing todos data:', error);
}
}
return [];
});
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);
const deleteTodo = (id) => {
setTodos((prevTodos) => {
const updatedTodos = prevTodos.filter((todo) => todo.id !== id);
localStorage.setItem('todos', JSON.stringify(updatedTodos)); // 변경된 todos 값을 로컬 스토리지에 저장
return updatedTodos;
});
};
const handleTodoSubmit = (e) => {
e.preventDefault();
const newTodo = e.target.elements.todo.value.trim();
if (newTodo) {
const todoItem = {
text: newTodo,
id: Date.now(),
};
setTodos((prevTodos) => {
const updatedTodos = [...prevTodos, todoItem];
localStorage.setItem('todos', JSON.stringify(updatedTodos)); // 변경된 todos 값을 로컬 스토리지에 저장
return updatedTodos;
});
e.target.reset();
}
};
return (
<>
<header>
<div id="weather">
<span className="CurrIcon">
<i className={weatherIcon ? WeatherIcons[weatherIcon] : ''}></i>
</span>
<span className="CurrTemp"> {temperature} </span>
<span className="City">{city}</span>
</div>
</header>
<main>
<div className="inner">
<div className="clock">
<h1 id="time">{time}</h1>
</div>
<div className="greeting">
{!username ? (
<form id="login_form" onSubmit={handleLoginSubmit}>
<input
required
maxLength="15"
type="text"
name="username"
placeholder="What is your name?"
/>
<button>Log in</button>
</form>
) : (
<h2 id="greetings">{greeting}</h2>
)}
</div>
<div className="todo">
<form id="todo_form" onSubmit={handleTodoSubmit}>
<input required type="text" name="todo" placeholder="Write a To Do and Press Enter." />
</form>
<ul id="todo_list">
{todos.map((todo) => (
<li key={todo.id}>
<span>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>❌</button>
</li>
))}
</ul>
</div>
</div>
</main>
</>
);
}
export default App;
3. 결과
Main
인사말
할 일 목록
4. 회고
React에 대해 배우면서 나도 아직 헷갈리는게 많다고 생각이 들었다. 바로바로 나와야하는데 안나오는 기분이랄까?
그래도 막 엄청나게 어렵다거나 그렇지는 않은거같다. 물론 css가 제일 귀찮ㅇ..ㅏ 이번에 Figma를 vscode에서 플러그인으로 사용할 수 있다길래 한번 써봤다.
Figma에서 설정되어있는 css를 바로 넣을 수 있다. 신기함 !! 혹시라도 제 과제의 회고를 보신다면 한번 해보시길.. 이번년도 까지만 무료라고 써져있음!
이정도면 퍼블리셔 없어지는거 아니냐구요ㅠㅠㅠ 그만 발전해.. 암튼 원래 컴포넌트를 나눠서 하려고했는데 하다보니 App.js에 다 때려넣어 버렸다😆
저번주에 과제가 있는 줄 모르고 안해버려서 저번주꺼 까지 같이 하느라 시간이 좀 오래 걸렸다. 슬랙에 매번 올려주셨었는데, 이번엔 안올라와서 없는 줄 알았다. 앞으로 노션을 잘 확이해봐야겠다ㅠ
내일은 프로젝트 설명회가 있는 날이라 기대된다. 재밌는 프로젝트를 선점해서 할 수 있는 기회가 왔으면 좋겠다! 앞으로도 화이팅!
본 후기는 유데미-스나이퍼팩토리 10주 완성 프로젝트캠프 학습 일지 후기로 작성 되었습니다.
#프로젝트캠프 #프로젝트캠프후기 #유데미 #스나이퍼팩토리 #웅진씽크빅 #인사이드아웃 #IT개발캠프 #개발자부트캠프 #리액트 #react #부트캠프 #리액트캠프
Leave a comment