useReducer
useState와 비슷한 상태관리 담당, useReducer의 장점은 상태 변경하는 로직을 다른 곳에서 처리할 수 있음. 즉, 컴포넌트의 상태 업데이트 로직을 컴포넌트에서 분리시킬 수 있다.
먼저 카운터 만들고 useReduce 를 사용해보고 비교해보기 !
1. 어제 만든 webpack 폴더에 - components 폴더 - counter 폴더 - Counter.jsx 파일 생성 및 아래 코드 작성
Counter.jsx
import React from 'react';
const Counter = () =>{
return(
<>
hi
</>
)
}
export default Counter
App.jsx
import React from 'react';
import Memo from './memo/memo';
import Counter from './components/counter/Counter'
const App = () => {
return(
<>
{/* <Memo /> */}
<Counter/>
</>
)
}
export default App
2. +1 -1 버튼 카운팅 만들기
import React from 'react';
const Counter = () =>{
const [number, setNumber] = React.useState(0)
const onUp = ()=>{
setNumber((prevNumber)=>prevNumber+1) // prevNumber 라는 변수명 아무거나 가능 !
}
const onDown = () =>{
setNumber((prevNumber)=>prevNumber+1)
}
return(
<>
<div>
<h2>{number}</h2>
<button onClick={onUp}> +1 </button>
<button onClick={onDown}> -1 </button>
</div>
</>
)
}
export default Counter
위의 코드는 비교용으로 남겨두기 ! -> 주석처리 후 아래에 똑같이 하나 만들기
3. 위의 코드를 상태값을 다르게 useReducer 사용해서 작성해보기
import React from 'react';
// const Counter = () =>{
// const [number, setNumber] = React.useState(0)
// const onUp = ()=>{
// setNumber((prevNumber)=>prevNumber+1) // prevNumber 라는 변수명 아무거나 가능 !
// }
// const onDown = () =>{
// setNumber((prevNumber)=>prevNumber+1)
// }
// return(
// <>
// <div>
// <h2>{number}</h2>
// <button onClick={onUp}> +1 </button>
// <button onClick={onDown}> -1 </button>
// </div>
// </>
// )
// }
const reducer = (number, action) =>{
// reducer는 총 두 가지 값을 받을 수 있음
// 첫 번째 number (상태값), 두 번째 type
// type에 맞게끔 상태값을 정하는 것
switch(action.type){
case 'UP':
return number +1;
case 'DOWN':
return number -1;
default:
return number;
}
}
const Counter =()=>{
// useReducer 기본 구문 ↓↓↓ useReducer 도 React 안에 있는 객체
// useRecucer의 인자값은 두가지.
// 첫번째 인자값 : 함수 (함수명 내가 임의로 정의할 수 있음 ex. reducer ~ )
// 두번째 인자값 : 기본 초기값 (useState랑 똑같) = > number 변수에 0 을 담음
// dispatch 라는 함수를 사용해서 내용을 바꾸게 되어있음
// useState 똑같아보이는데 장점은 ? -> 로직 분리
// useReduce 의 함수는 Component 밖에 있어도 상관없다.
const [number, dispatch] = React.useReducer(reducer,0)
const onUp = () =>{
//setState({}) 와 비슷하게 바꿀 내용 객체값으로 넣기
dispatch({ type: 'UP' }) // -> 요건 reducer 함수의 두 번째 인자값
// dispatch를 호출하면 reducer 함수를 호출하게 됨
}
const onDown =() =>{
// type 이 DOWN 이라는 걸로 할 꺼야 ( ? 이게 무슨 ? )
dispatch({ type: 'DOWN'})
}
return(
<>
<div>
<h2>{number}</h2>
<button onClick={onUp}> +1 </button>
<button onClick={onDown}> -1 </button>
</div>
</>
)
}
export default Counter
음..... 저 안에 교수님의 설명을 다 적었는데 이해가 어렵,,,,,,,,,,,,,,,
복습하면서 뜯어봐야징
4. Component 밖에 두었던 reducer 함수를 아예 다른 파일로 빼기
counter 폴더 안 reducer.js 파일 생성 - reducer 함수 복붙 + 'export' 코드 추가
reducer.js (교수님은 jsx 로 해야 오류가 안났다 -> 왜징 ??? -> webpack 환경설정 차이 !)
export const reducer = (number, action) =>{
// reducer는 총 두 가지 값을 받을 수 있음
// 첫 번째 number (상태값), 두 번째 type
// type에 맞게끔 상태값을 정하는 것
switch(action.type){
case 'UP':
return number +1;
case 'DOWN':
return number -1;
default:
return number;
}
}
Counter.jsx 에 reducer.js 불러오기
import React from 'react';
import {reducer} from './reducer'
reducer 장점 : 상태를 관리하는 함수를 따로 파일로 빼서 관리할 수 있는 장점 ! 특히 Context와 함께 같이 쓴다.
5. string 도 변수로 빼기
Counter.jsx
import React from 'react';
import {reducer, UP, DOWN} from './reducer'
const Counter =()=>{
const [number, dispatch] = React.useReducer(reducer,0)
const onUp = () =>{
//setState({}) 와 비슷하게 바꿀 내용 객체값으로 넣기
dispatch({ type:UP }) // -> 요건 reducer 함수의 두 번째 인자값
// dispatch를 호출하면 reducer 함수를 호출하게 됨
}
const onDown =() =>{
// type 이 DOWN 이라는 걸로 할 꺼야 ( ? 이게 무슨 ? )
dispatch({ type: DOWN})
}
return(
<>
<div>
<h2>{number}</h2>
<button onClick={onUp}> +1 </button>
<button onClick={onDown}> -1 </button>
</div>
</>
)
}
export default Counter
reducer.js
export const UP = 'UP'
export const DOWN = 'DOWN'
export const reducer = (number, action) =>{
// reducer는 총 두 가지 값을 받을 수 있음
// 첫 번째 number (상태값), 두 번째 type
// type에 맞게끔 상태값을 정하는 것
switch(action.type){
case 'UP':
return number +1;
case 'DOWN':
return number -1;
default:
return number;
}
}
export default = 1개만 보내기
or 아래처럼 여러개 보내기
Context
Props 하위 컴포넌트에 보내는 것을 편리하게 만들어주는 Context !
<- App에서의 내용을 가장 아래 컴포넌트에서 바로 사용 가능하도록 !
함수형 Context (클래스 Context는 7월 9일 수업에 함 !)
1. 위의 그림대로 Components 만들기
Component 파일에 - context 폴더 - Layout.jsx 생성 / 코드 입력
import React from 'react';
const Layout = () =>{
return(
<>
<LoginBox/>
</>
)
}
const LoginBox =()=>{
return(
<>
<Login/>
</>
)
}
const Login = () =>{
return(
<>
<Button/>
</>
)
}
const Button = () =>{
return(
<>
<buuton>HI CONTEXT</buuton>
</>
)
}
export default Layout
2. context 파일 안에 LayoutContext.js 파일 생성 / 아래 코드 작성
//4개의 컴포를 담을 box를 만들기 위해 요 파일을 만듬.
// 1.createContext() 현실적으로 4개 컴포를 감싸는 주체가 되는 컴포넌트
// 가장 최상위 컴포넌트 - LayoutContext가 되어야함. <- 얘한테 직접적으로 요청함 by 하위 Components
// LayoutStore는 껍데기일 뿐 !
// props.children 써서 사용하고 싶어서 !
// LayoutContext.Provide value 에 실질적으로 데이터를 넣는 공간.
import React from 'react';
export const LayoutContext = React.createContext() // context 생성하기=하나의 컴포넌트 생성했따는 것
const LayoutStore =(props)=>{
const user={
userid:'asdf',
username:'addd',
job:'dddd'
}
return(// //LayoutContext도 컴포넌트라 jsx 형태로 이렇게 쓰기 가능 !
// 기본값으로 무엇을 보낼지 value
<LayoutContext.Provider value={user}>
{props.children}
</LayoutContext.Provider>
)
}
export default LayoutStore
Layout.jsx
import React from 'react';
import LayoutStore, {LayoutContext} from './LayoutContext'; //추가
const Layout = () => {
return (
<>
<LayoutStore>
<LoginBox />
</LayoutStore>
</>
)
}
const LoginBox = () => {
return (
<>
<Login />
</>
)
}
const Login = () => {
return (
<>
<Button />
</>
)
}
const Button = () => {
// 인자값 : LayoutContext를 넣어주기 ->
const context = React.useContext(LayoutContext)
return (
<>
<button>HI CONTEXT</button>
<ul>
<li>{context.userid}</li>
<li>{context.username}</li>
<li>{context.job}</li>
</ul>
</>
)
}
export default Layout
App.jsx
import React from 'react';
import Memo from './memo/memo';
import Counter from './components/counter/Counter'
import Layout from './components/context/Layout'
const App = () => {
return(
<>
{/* <Memo /> */}
{/* <Counter/> */}
<Layout />
</>
)
}
export default App
context 만으로 가능
context에게 요청을 바로 해서 context 의 값을 가져오는 방식
Styled Component
CSS 에 처리에 대한 내용
* 일반 CSS 넣기
1. npm 설치
$ npm i styled-components
2. components 폴더 안 styleComponent 파일 > index.jsx 생성 / 코드 작성
import React from 'react'
const Index = () =>{
return(
<>
hi sytled
</>
)
}
export default Index
3. App Component에 연결
import React from 'react';
import Memo from './memo/memo';
import Counter from './components/counter/Counter'
import Layout from './components/context/Layout'
import Index from './components/styledComponent/index'
4. index.jsx
button에 직접 style 넣기 (styled-component 사용 X)
import React from 'react'
const Buttonstyle = {
"background":"black",
"border":"none",
"color":"white",
"padding":"7px 14px",
}
const Index = () =>{
return(
<>
hi sytled
<div >
<input type="text"/>
<button style={Buttonstyle}>
hi
</button>
</div>
</>
)
}
export default Index
styled-components 사용하기
1. styled-components 가져오기
import React from 'react'
import styled from 'styled-components'
index.jsx
import React from 'react'
import styled from 'styled-components'
const Buttonstyle = {
"background":"black",
"border":"none",
"color":"white",
"padding":"7px 14px",
}
const Button = styled.button`
background:#000;
border:none;
color:#fff;
padding:7px 14px;
`
const Index = () =>{
return(
<>
hi sytled
<div >
<input type="text"/>
<button style={Buttonstyle}>
hi
</button>
<Button>
hihi
</Button>
</div>
</>
)
}
export default Index
// < = 야는 컴포넌트를 뜻함. styled 안에 있는 button 버튼에 아래와 같은 스타일을 적용한 Button이라는 것을 만듬
application check -> component 안에서만 유효한 css
css 파일과 겹치지 않아서 협업할 때 좋음
2. hover 해보기
import React from 'react'
import styled from 'styled-components'
const Buttonstyle = {
"background": "black",
"border": "none",
"color": "white",
"padding": "7px 14px",
}
const Button = styled.button`
background:#000;
border:none;
color:#fff;
padding:7px 14px;
`
const ButtonHover = styled(Button)`
background:#007bff;
:hover{
background:#0069d9;
}
`
const Index = () => {
return (
<>
hi sytled
<div >
<input type="text" />
<button style={Buttonstyle}>
hi
</button>
<Button>
hihi
</Button>
<ButtonHover>
hoho
</ButtonHover>
</div>
</>
)
}
export default Index
3. 이제 DOM 조작을 해보기 -> 위의 첫 번째 버튼 클릭하면 input box cursor가 가도록 만들기
index.jsx
const Index = () => {
const inputRef = React.useRef() // element 넣을 준비 되어 있어 !
const handleClick=()=>{
console.log(inputRef.current)
}
return (
<>
hi sytled
<div >
<input type="text" ref={inputRef}/>
<button
style={Buttonstyle}
onClick={handleClick}
>
hi
</button>
<Button>
hihi
</Button>
-
const Index = () => {
const inputRef = React.useRef() // element 넣을 준비 되어 있어 !
const handleClick=()=>{
//inputRef.current = 해당 element
inputRef.current.focus()
inputRef.current.style.height = '30px'
// inputRef.current.style.display='none'
}
return (
<>
hi sytled
<div >
<input type="text" ref={inputRef}/>
<button
style={Buttonstyle}
onClick={handleClick}
-
const Index = () => {
const inputRef = React.useRef() // element 넣을 준비 되어 있어 !
const handleClick=()=>{
//inputRef.current = 해당 element
inputRef.current.focus()
inputRef.current.style.height = '30px'
if(inputRef.current.style.display=='block'){
inputRef.current.style.display = 'none'
}else{
inputRef.current.style.display ='block'
}
}
두 번째 버튼은 익명함수로 주기
<div >
<input type="text" ref={inputRef}/>
<button
style={Buttonstyle}
onClick={handleClick}
>
hi
</button>
<Button onClick={()=>{handleClick()}}>
hihi
</Button>
전체 코드
import React from 'react'
import styled from 'styled-components'
const Buttonstyle = {
"background": "black",
"border": "none",
"color": "white",
"padding": "7px 14px",
}
const Button = styled.button`
background:#000;
border:none;
color:#fff;
padding:7px 14px;
`
const ButtonHover = styled(Button)`
background:#007bff;
:hover{
background:#0069d9;
}
`
const Index = () => {
const inputRef = React.useRef() // element 넣을 준비 되어 있어 !
const handleClick=()=>{
//inputRef.current = 해당 element
inputRef.current.focus()
inputRef.current.style.height = '30px'
if(inputRef.current.style.display=='block'){
inputRef.current.style.display = 'none'
}else{
inputRef.current.style.display ='block'
}
}
return (
<>
hi sytled
<div >
<input type="text" ref={inputRef}/>
<button
style={Buttonstyle}
onClick={handleClick}
>
hi
</button>
<Button onClick={()=>{handleClick()}}>
hihi
</Button>
<ButtonHover>
hoho
</ButtonHover>
</div>
</>
)
}
export default Index
Tic Tac Toe 게임을 위에서 배운 내용들로 만들어 보기
component >game > 3개 comps 만들기
1. 위의 파일트리 만들기 / 각 내용 만들기 / 코드 작성 및 연결
App > Game > Board > Square
2. 화면 부터 만들기 -> React할 때 기본 !
Game.jsx
import React from 'react'
import Board from './Board'
import Styled from 'styled-components';
// wrap : 영역에서 넘어가면 떨군다.
const GameDiv = Styled.div`
display:flex;
flex-wrap:wrap;
align-item:center;
justify-content:center;
width:300px;
`
const Game = () => {
return (
<GameDiv>
<Board />
</GameDiv>
)
}
export default Game
Board
import React from 'react'
import Square from './Square'
const Board = () =>{
return(
<>
<Square />
<Square />
<Square />
<Square />
<Square />
<Square />
<Square />
<Square />
<Square />
</>
)
}
export default Board
Square
import React from 'react'
import Styled from 'styled-components'
const Button = Styled.button`
width:33.3333%;
height:70px;
border:1px solid #000;
background:#fff;
cursor:pointer;
font-size:30px;
`
const Square = () => {
return (
<>
<Button>
0
</Button>
</>
)
}
export default Square
화면 그리기 끝
3. 로직 처리하기
Game Component에 reducer 사용해보기
Game.jsx
import React from 'react'
import Board from './Board'
import Styled from 'styled-components';
// wrap : 영역에서 넘어가면 떨군다.
const GameDiv = Styled.div`
display:flex;
flex-wrap:wrap;
align-item:center;
jstify-content:center;
width:300px;
`
const reducer = (state, action) => {
}
const defaultState = {
squares: Array(9).fill(null),
xIsNext: true,
winner: null,
}
const Game = () => {
const [state, dispatch] = React.useReducer(reducer, defaultState)
return (
<>
<GameDiv>
<Board />
</GameDiv>
{state.winner ? `${state.winner}님 승리`:`Next Player is ${state.xIsNext ? 'X' : 'O'}`}
</>
)
}
export default Game
이제 winner 값만 채워주면 ~ 가 승리! 를 알게됨
div 클릭하면 'O승리' 나오게 만들기 onClick 함수 사용
const reducer = (state, action) => {
switch(action.type){
case 'winner':
return {
...state,
winner:action.ox
}
}
}
const defaultState = {
squares: Array(9).fill(null),
xIsNext: true,
winner: null,
}
const Game = () => {
const [state, dispatch] = React.useReducer(reducer, defaultState)
const handleClick =() =>{
dispatch({type:'winner', ox:'O'})
}
return (
<>
<GameDiv onClick={()=>handleClick()}>
game.jsx
return (
<>
<GameDiv >
<Board
squares={state.squares}
/>
다시 onClick 이벤트 삭제하고 squares 보내기
board.jsx
const Board = (props) =>{
return(
<>
그런데 객체로 그대로 받기
const Board = ({squares}) =>{
return(
배열 뿌리기
import React from 'react'
import Square from './Square'
const Board = ({ squares }) => {
const square = squares.map((v, k) => {
return (
<Squares
key={k}
value={v}
/>
)
})
return (
<>
{square}
</>
)
}
export default Board
Square.jsx
import React from 'react'
import Styled from 'styled-components'
const Button = Styled.button`
width:33.3333%;
height:70px;
border:1px solid #000;
background:#fff;
cursor:pointer;
font-size:30px;
`
const Square = ({value}) => {
return (
<>
<Button>
{value}
</Button>
</>
)
}
export default Square
handleClick 내용 완성시키고 Square 쪽까지 전달 시키기
Game
const handleClick =(n) =>{ // n = > key값 1~ 9 -> Board 콤프에 있음
dispatch({type:'NEXT', index:n})
}
return (
<>
<GameDiv >
<Board
squares={state.squares}
onClick ={handleClick}
Baord.jsx
import React from 'react'
import Square from './Square'
const Board = ({ squares, onClick }) => {
const square = squares.map((v, k) => {
return (
<Square
key={k}
value={v}
onClick={()=>onClick(k)}
/>
)
})
return (
<>
{square}
</>
)
}
export default Board
Sqaure.jsx
import React from 'react'
import Styled from 'styled-components'
const Button = Styled.button`
width:33.3333%;
height:70px;
border:1px solid #000;
background:#fff;
cursor:pointer;
font-size:30px;
`
const Square = ({value, onClick}) => {
return (
<>
<Button onClick={()=>onClick()}>
{value}
</Button>
</>
)
}
export default Square
클릭하면 잘 찍히는지 확인
const Game = () => {
const [state, dispatch] = React.useReducer(reducer, defaultState)
const handleClick =(n) =>{ // n = > key값 1~ 9 -> Board 콤프에 있음
// dispatch({type:'NEXT', index:n})
console.log(n)
}
이제 O, X 찍히도록 만들기
Game
const reducer = (state, action) => {
switch(action.type){
case 'NEXT':
const {squares} = {...state}
squares[action.index] = state.xIsNext ? 'X' : 'O'
return {
...state,
xIsNext:!state.xIsNext,
squares,
}
}
}
내가 클릭한 곳이 또 클릭한 경우 || 승자가 결정된 경우, 함수 실행안되도록 만들기
const Game = () => {
const [state, dispatch] = React.useReducer(reducer, defaultState)
const handleClick =(n) =>{ // n = > key값 1~ 9 -> Board 콤프에 있음
if(state.squares[n]) return;
if(state.winner !== null) return;
dispatch({type:'NEXT', index:n})
console.log(n)
}
승자 결정하기
위너 정하는 Winner함수
import React from 'react'
import Board from './Board'
import Styled from 'styled-components';
// wrap : 영역에서 넘어가면 떨군다.
const GameDiv = Styled.div`
display:flex;
flex-wrap:wrap;
align-item:center;
jstify-content:center;
width:300px;
`
const reducer = (state, action) => {
switch(action.type){
case 'NEXT':
const {squares} = {...state}
squares[action.index] = state.xIsNext ? 'X' : 'O'
return {
...state,
xIsNext:!state.xIsNext,
squares,
}
case 'win':
return{
...state,
winner:action.winner
}
}
}
const defaultState = {
squares: Array(9).fill(null),
xIsNext: true,
winner: null,
}
const Winner =(squares)=>{
let lines = [
[0,1,2],
[3,4,5],
[6,7,8],
[0,3,6],
[1,4,7],
[2,5,8],
[0,4,8],
[2,4,6]
]
for(let i=0; i<lines.length; i++){
let [a,b,c] = lines[i]
//squares[a] -> 해당 값이 있는지 확인하는 요소
if(squares[a] && squares[a]==squares[b] && squares[a]==squares[c]){
return squares[a]
}
}
return null
}
const Game = () => {
const [state, dispatch] = React.useReducer(reducer, defaultState)
const handleClick =(n) =>{ // n = > key값 1~ 9 -> Board 콤프에 있음
if(state.squares[n]) return;
if(state.winner !== null) return;
dispatch({type:'NEXT', index:n})
console.log(n)
}
//첫 번째 인자값 : 실행할 함수 두 번째 인자값 : 기준이될 함수 - 바뀌면 실행함
React.useEffect(()=>{
console.log('we')
const win = Winner(state.squares)
if(!win){
dispatch({type:'win', winner:win})
}
},[state.xIsNext])
return (
<>
<GameDiv >
<Board
squares={state.squares}
onClick ={handleClick}
/>
</GameDiv>
{state.winner ? `${state.winner}님 승리`:`Next Player is ${state.xIsNext ? 'X' : 'O'}`}
</>
)
}
export default Game
'블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기' 카테고리의 다른 글
[87일차]20210714 Context로 댓글 만들기 (with 리액트식으로 컴포넌트 구성하기) (0) | 2021.07.14 |
---|---|
[86일차 복습] React useReducer Context syled-component로 TicTacToe만들기 (0) | 2021.07.14 |
[85일차 복습] React Hooks useMemo useCallback (0) | 2021.07.13 |
[85일차]20210712 React hooks API useMemo useCallback (0) | 2021.07.12 |
[84일차]20210708 (0) | 2021.07.12 |