반응형

이번내용은 리액트에 스타일을 줄 수 있는 방법들이다

단순히 css파일로 이나 인라인태그로 스타일을 주는 게 아니라 

css를 좀 더 편하게 작성하고 유동적으로 적용할 수 있는 툴을 배워보자

 

sass 를 만들어줄 리액트 프로젝트 폴더 위치에서 설치해 준다

 

sass 설치방법

C:\Users\사용자폴더\Desktop\react\StyledComponent> npm install node-sass@8

cmd에서 cd로 설치할 폴더로 이동해 준 뒤에 npm install node-sass@8을 해준다

@8은 버전인데 설치되어 있는 Node.js의 버전에 따라 설치해주면 된다. 나의 버전은 v18.12.1이다

Node.js의 버전을 확인하는 방법은 터미널에 node -v를 입력해 주면 확인이 가능하다

styled-components 설치방법

위와 똑같이 사용할 프로젝트폴더의 위치로 이동한 뒤에 

npm install --save styled-components를 입력해 주면 된다.

 

설치가 잘되었는지 확인하는 방법은 프로젝트 폴더의 package.json에 들어가면 dependencies에 들어가 있는 걸 볼 수 있다.

sass,styledcomponent설치 확인

 

SaSS--Syntactically Awesome Style Sheets

css작성을 간단하게 해 주고 변수선언이 간편하고 함수처럼 스타일을 적용시켜 줄 수도 있다. 

Sass는 CSS 전처리기(Preprocessor)라고도 부른다
CSS가 만들어지기 전에 이런저런 일들을 먼저 처리해 주기 때문

일반 html에서 쓰려면 sass를 작성하고 컴파일해서 css로 바꿔주는 과정이 필요한데

리액트에서 쓰는 node-sass는 그런 과정이 필요 없이 작성한 sass를 바로 적용할 수 있다.

 

SassComponent.js

import React from 'react';
import './SassComponent.scss'

const SassComponent = () => {
    return (
        <div className='SassComponent'>
            <div className='box red'></div>
            <div className='box orange'></div>
            <div className='box yellow'></div>
            <div className='box green'></div>
            <div className='box blue'></div>
            <div className='box indigo'></div>
            <div className='box violet'></div>
        </div>
    );
};
export default SassComponent;

스타일을 적용시켜 줄 컴포넌트를 만들어줬다 스타일을 줄 클래스도 만들어주고

만들어질 scss를 import 시켜줬다.

 

SassComponent.scss

//변수 사용하기
$red : #fa5252;
$orange : #fd7e14;
$yellow : #fcc419;
$green : #40c057;
$blue : #339af0;
$indigo : #5c7cfa;
$violet : #7950f2;

@mixin square($size){ 
    $calculate: 32px * $size;
    width: $calculate;
    height: $calculate;
}
.SassComponent {
    display: flex;
    .box{
        background-color: red;
        cursor: pointer;
        transition: 0.3s;
        &.red{
            background: $red;
            @include square(1);
        }
        &.orange{
            background: $orange;
            @include square(2);
        }
        &.yellow{
            background: $yellow;
            @include square(3);
        }
        &.green{
            background: $green;
            @include square(4);
        }
        &.blue{
            background: $blue;
            @include square(5);
        }
        &.indigo{
            background: $indigo;
            @include square(6);
        }
        &.violet{
            background: $violet;
            @include square(7);
        }
        &:hover{
            background: black;
        }
    }    
}

@mixin ) mixin(믹스인)은 함수와 비슷한 동작을 하는 문법

CSS 스타일 시트에서 반복적으로 재사용할 CSS 스타일 그룹 선언을 정의하는 기능을 해준다.

@mixin으로 만들고 @include로 호출해서 사용 파라미터를 넣어줄 수도 있다.

위 구문에서는 파라미터로 숫자를 받아서 스타일크기가 숫자배수가 되도록 만들어줬다.

Sass는 하위요소를 안쪽으로 들여 써서 한 번에 작성해 줄 수 있다

$로 변수를 만들어서 값을 저장할 수 있다.

 

. SassComponet 안에 있는. box클래스를 들여서 써주고  &. violet =. box.violet과 같은 역할을 해준다 

 

& 는 자기 자신을 의미한다.

 

완성화면)

 

 

Styled-components

styledcomponents

styled-components는 style이 적용된 Component를 직접 생성하기 때문에, 스타일링을 위한 코드 사용량이 줄어드는 효과가 있습니다. 또 key value의 형태가 아닌 css의 문법을 그대로 사용하기 때문에
기존 css의 사용법보다 
가독성도 높습니다.

 

사용법

import "./styles.css";
import styled from "styled-components";
const Circle = styled.div`
  width: 50px;
  height: 50px;
  background: black;
  border-radius: 50%;
`;
export default function App() {
  return (
    <div className="App">
      <Circle />
    </div>
  );
}

 

스타일이 적용된 컴포넌트를 만들어준다고 생각하면 이해하기가 조금 쉽다

 

컴포넌트이기 때문에 props전달도 가능하다  , CSS사용

import  styled,{css}  from 'styled-components';

const CircleDiv = styled.div`
width:50px;
height:50px;
background: ${props=> props.color || 'black'}; props.color가 있으면 적용 없으면 black
border-radius:50%;
${props => props.big && //props.big이 true이면 별도의 css구문 실행
css`
  width:150px;
  height:150px;
`
}
`;

function Circle() {
  return (
    <div>
        <CircleDiv color='blue'/>
        <CircleDiv color='red' big/>
        <CircleDiv/>
    </div>
  );
}

export default Circle;

 

여러 줄의 CSS 코드를 조건부로 보여주고 싶다면 css를 사용해야합니다. css 를 불러와서 사용을 해야
그 스타일 내부에서도 다른 props를 조회할 수 있습니다.

CircleDiv에 props를 주면 위의 컴포넌트선언부에서 props를 받아서 조건문에 사용해서 적용해 준다.

 

ThemeProvider

 색상 코드를 지닌 변수를 Button.js 에서 선언을 하는 대신에 ThemeProvider라는 기능을 사용하여 styled-components로 만드는 모든 컴포넌트에서 조회하여 사용할 수 있는 전역적인 값을 설정해 보겠습니다.

    <ThemeProvider theme={{
      palette:{
        blue:'#228be6',
        gray:'#495057',
        pink:'#f06595'
      }
    }}>
    </ThemeProvider>

ThemeProvider 내부에 렌더링 된 styled-components로 만든 컴포넌트에서 

palette를 조회하여 사용할 수 있습니다

props.theme.pallete.blue 이런 식으로 값에 접근이 가능하다.

 

예제)

좀 긴 코드라 차근차근 만들어서 보여주겠다..

import styled, { css } from "styled-components";
const AppBlock = styled.div`
  width: 512px;
  margin: 0 auto;
  margin-top: 4em;
  border: 1px solid black;
  padding: 1em;
`;

export default function App() {
  return (
    <AppBlock>
      <div></div>
    </AppBlock>
  );
}

AppBlock이라는 div  styled-component를 만들어준다. 

styledcomponent - AppBlock

 

이제 저 빈 상자 안에 넣어줄 버튼컴포넌트를 만들어 준다

 

ButtonTotal.js

import React from "react";
import styled, { css } from "styled-components";

const ButtonTotal = ({ children }) => {
  const StyledButton = styled.button`
    /*공통스타일*/
    display: inline-flex;
    outline: none;
    border: none;
    border-radius: 4px;
    color: white;
    font-weight: bold;
    cursor: pointer;
    padding: 1em;
    margin: 10px;

    /*크기*/
    font-size: 1em;

    /*효과*/
    &:hover {
      background: #1c7ed6;
    }
  `;

  return <StyledButton>{children}</StyledButton>;
};

export default ButtonTotal;

 

styledButton을 만들어서 마운트시켜줬다.  App.js에서 태그사이글자를

props.children으로 받아서 내용을 넣어준다.

import styled, { css } from "styled-components";
import ButtonTotal from './ButtonTotal'
const AppBlock = styled.div`
  width: 512px;
  margin: 0 auto;
  margin-top: 4em;
  border: 1px solid black;
  padding: 1em;
`;

export default function App() {
  return (
    <AppBlock>
      <div>
        <ButtonTotal>Button</ButtonTotal>
      </div>
    </AppBlock>
  );
}

 ButtonTotal을 import 해서 마운트 시켜준다 Button글자를 props로 보내준다.

호버이벤트가 걸려있어서 마우스가 올라가면 색이 변함

이제 이 버튼을 여러 개 만들어주고 각각 props를 줘서 크기와 색을 다르게 해 준다.

같은모양의 버튼이 여러개~

 

우선은 크기부터 변경해 보자.

 

App.js

import styled, { css } from "styled-components";
import ButtonTotal from "./ButtonTotal";
const AppBlock = styled.div`
  width: 512px;
  margin: 0 auto;
  margin-top: 4em;
  border: 1px solid black;
  padding: 1em;
`;

export default function App() {
  return (
    <AppBlock>
      <div>
        <ButtonTotal size="large">Button</ButtonTotal>
        <ButtonTotal>Button</ButtonTotal>
        <ButtonTotal size="small">Button</ButtonTotal>
      </div>
      <div>
        <ButtonTotal size="large">Button</ButtonTotal>
        <ButtonTotal>Button</ButtonTotal>
        <ButtonTotal size="small">Button</ButtonTotal>
      </div>
      <div>
        <ButtonTotal size="large">Button</ButtonTotal>
        <ButtonTotal>Button</ButtonTotal>
        <ButtonTotal size="small">Button</ButtonTotal>
      </div>
    </AppBlock>
  );
}

App에서 props로 size를 보내준다 size props가 없는 컴포넌트는

ButtonTotal에서 size props 디폴트값을 medium으로 준다

 

ButtonTotal.js

import React from "react";
import styled, { css } from "styled-components";

const ButtonTotal = ({ children, size }) => {
  const StyledButton = styled.button`
    /*공통스타일*/
    display: inline-flex;
    outline: none;
    border: none;
    border-radius: 4px;
    color: white;
    font-weight: bold;
    cursor: pointer;
    padding: 1em;
    margin: 10px;

    /*크기*/
    font-size: 1em;

    /*조건별크기*/
    ${(props) => {
      return (
        props.size === "large" &&
        css`
          height: 3.5em;
          font-size: 1.5em;
          width: 40%;
        `
      );
    }}
    ${(props) => {
      return (
        props.size === "medium" &&
        css`
          height: 2.25em;
          font-size: 1em;
        `
      );
    }}
    ${(props) => {
      return (
        props.size === "small" &&
        css`
          height: 1.5em;
          font-size: 0.75em;
        `
      );
    }}
    /*효과*/
    &:hover {
      background: #1c7ed6;
    }
  `;

  return <StyledButton size={size}>{children}</StyledButton>;
};
ButtonTotal.defaultProps = {
  size: "medium"
};
export default ButtonTotal;

Props를 app에서 ButtonTotal로 보내주면 ButtonTotal > StyledButton으로 보내주고

StyledButton에서는 그걸 받아서 조건으로 props.size와 텍스트가 일치하면

css 구문을 적용시켜 주는 방식이다.

 

이제 색상을 각각 다르게 적용해 주겠다. 여기서는 ThemeProvider을 이용해 주겠다.

App.js

import styled, { ThemeProvider } from "styled-components";
import ButtonTotal from "./ButtonTotal";
const AppBlock = styled.div`
  width: 512px;
  margin: 0 auto;
  margin-top: 4em;
  border: 1px solid black;
  padding: 1em;
`;

export default function App() {
  return (
    <ThemeProvider
      theme={{
        palette: {
          blue: "#228be6",
          gray: "#495057",
          pink: "#f06595"
        }
      }}
    >
      <AppBlock>
        <div>
          <ButtonTotal size="large">Button</ButtonTotal>
          <ButtonTotal>Button</ButtonTotal>
          <ButtonTotal size="small">Button</ButtonTotal>
        </div>
        <div>
          <ButtonTotal size="large" color="pink">
            Button
          </ButtonTotal>
          <ButtonTotal color="pink">Button</ButtonTotal>
          <ButtonTotal size="small" color="pink">
            Button
          </ButtonTotal>
        </div>
        <div>
          <ButtonTotal size="large" color="gray">
            Button
          </ButtonTotal>
          <ButtonTotal color="gray">Button</ButtonTotal>
          <ButtonTotal size="small" color="gray">
            Button
          </ButtonTotal>
        </div>
      </AppBlock>
    </ThemeProvider>
  );
}

 

ThemeProvider를 import 해주고 theme객체를 만들어주었다

ThemeProvider컴포넌트 내부에 있는 컴포넌트들은 props로 값에 접근이 가능하다.

어떤 색을 줄지 알려주기 위해 colorprops로 보내준다.

import React from "react";
import styled, { css } from "styled-components";

const ButtonTotal = ({ children, size, color, ...rest }) => {
  const StyledButton = styled.button`
    /*공통스타일*/
    display: inline-flex;
    outline: none;
    border: none;
    border-radius: 4px;
    color: white;
    font-weight: bold;
    cursor: pointer;
    padding: 2em;
    margin: 10px;

    /*크기*/
    font-size: 1em;
    /* 조건별색깔 */
    ${({ theme, color }) => {
      const selectcolor = theme.palette[color];
      return css`
        background: ${selectcolor};
      `;
    }}

    /*조건별크기*/
    ${(props) => {
      return (
        props.size === "large" &&
        css`
          height: 3.5em;
          font-size: 1.5em;
          width: 40%;
        `
      );
    }}
    ${(props) => {
      return (
        props.size === "medium" &&
        css`
          height: 2.25em;
          font-size: 1em;
        `
      );
    }}
    ${(props) => {
      return (
        props.size === "small" &&
        css`
          height: 1.5em;
          font-size: 0.75em;
        `
      );
    }}
    /*효과*/
    &:hover {
      background: #1c7ed6;
    }
  `;

  return (
    <StyledButton size={size} color={color} {...rest}>
      {children}
    </StyledButton>
  );
};
ButtonTotal.defaultProps = {
  size: "medium",
  color: "blue"
};
export default ButtonTotal;

color props를 못 받은 컴포넌트는 default로 blue를 준다.

color props를 받아서 보내주고 themeprovider... rest로 받아서 보내준다.

/* 조건별색깔 */
    ${({ theme, color }) => {
      const selectcolor = theme.palette[color];
      return css`
        background: ${selectcolor};
      `;
    }}

이 부분에서 받아서 theme.palette 값에 color로 접근한 다음 나온 값을 selectcolor 에저장하고

selectcolor를 백그라운드값으로 적용하는 구문을 반환해 준다.

 

조금 바꾼 부분이 있는데 ButtonTotal.js에서 스타일컴포넌트 만드는 부분을

ButtonTotal컴포넌트 부분 바깥으로 빼줘야 한다.

안쪽에 넣어서 썼더니 스타일이 계속 참조된다 그랬나? 새로 스타일을 만들어줘야 된다는 경고문이 떠서

바깥으로 꺼내줬더니 해결되었다. 

 

한 번에 이해하기는 조금 어려운 부분 이긴 한데 값을 전달받고 쓰는 방법이 조금 어려운 거지 조건문이나  css자체는 이미 다해봤던 익숙한 내용들이라 익숙해지기만 하면 사용하는 데에 크게 문제는 없을 것 같다.

반응형
반응형

오늘은 useState,useEffect,useRef를 복습하고 새로운 Hook함수로 useContext를 배웠다. 

일단 복습한내용부터 정리하고 새로배운내용을 정리해보자.

 

useState ----->상태관리,[ state , setState( f ) ] 

상태값과 상태값을 관리하는 함수를 리턴해줌 ( [함수형에서는 배열] , {클래스형에서는 객체} )


useEffect ----> 마운트, 언마운트, 값업데이트 (리렌더링) 될때 실행코드 작성

상태가 변경될때마다 실행되는함수 여러조건으로 실행할수있음

 

마운트 , 업데이트시 콜백함수 호출 

useEffect(()=>{
	//마운트 , 업데이트시 실행될구문	
})


마운트될때만 콜백함수 호출

useEffect(()=>{
	//컴포넌트가 마운트될때 한번만 실행
},[])


마운트 될때와 상태값이 변경될때만 호출

useEffect(()=>{
	//state값이 변경될때만 실행
},[state])

 코드 예제) 각useEffect들은 렌더링될때,처음마운트될때,number상태값이 변경될때 실행된다

input에 값을 넣을때도 state가 변경되는 함수가 걸려있기때문에 렌더링된다. (그래서 콘솔보면 1번이 출력될꺼임)

콘솔에 출력되도록 해놨으니 꼭 콘솔키고 확인하자!

 

마운트될때 호출 , 언마운트 될때 클린업 호출

useEffect(()=>{
	//마운트될때 실행문
	return( ) => {
		//언마운트될때 실행문
	}
})

 

코드 예제)

버튼을 클릭하면 Timer 컴포넌트 가 마운트되고 useEffecttimer함수가 실행된다. 

다시버튼을 클릭하면 showtimer가 false로 바뀌고 상태값이바뀌면서 다시렌더링되게된다 false이므로 Timer는 언마운트 되고 Timer 컴포넌트의 useEffect에서 언마운트될때의 구문을 실행시켜서 timersetinterval을 지워준다.

useRef ---->컴포넌트의 전 생애주기동안 유지됨 , Ref값은 변경되어도 리렌더링 되지않음

사용 용도
1)저장공간
const count = useRef( 10 )  ==> 객체리턴{ current: 10 }
count.current = count.current + 1   //current 키로 값에 접근한다
2)DOM요소 접근
const inputName = useRef( );
<input ref = { inputName } />

Document.querySelector("#inputname").focus( );
inputName.current.focus( );         두개의 동작이 같음

 

복습끝.


여기서 부터는 오늘 배운 내용

useContext

ContextAPI 
App을 루트로하는 트리구조로 컴포넌트를 이용하는데  props를 하위단계로 계속넘겨준다
n레벨에서는 props값을 n번 전달해줘야한다. (비효율적)

 

ContextAPI는 최상위 컴포넌트에있는 값을 하위컴포넌트 어디서든 사용할수있게 해준다. 

어떤 컴포넌트 트리내에서 최상위 컴포넌트 부터 최 말단 컴포넌트에 걸쳐 
전역으로 관리해야할 데이터를 관리.

1)context생성
-context : 전역 데이터를 담고있는 저장공간
 createContext(초기값) 함수호출

import { createContext } from "react";
export const NameContext = createContext("green")

 

2)context사용
useContext(context)

import { NameContext } from '../context/NameContext'

const Header = ()=>{
	const siteName = useContext
    return(
    	<div>
        	<h1>siteName<h1>
        </div>
    )
}

 

Context 객체 안에는 Provider라는컴포넌트가 들어있습니다. 

그 컴포넌트간에 공유하고자 하는 값을 value 라는 Props로 설정하면 자식 컴포넌트들에서

해당 값에 바로 접근을 할 수 있습니다.

function App() {
  return (
    <NameContext.Provider value="Hello World">
      <GrandParent />
    </NameContext.Provider>
  );
}

원하는 컴포넌트에서 useContext 를 사용하여 Context에 넣은 값에 바로 접근할 수 있습니다. 
NameContext.Provider 로 접근해서 value로 값을 바꿀수있다.

좀 이해하기 쉽게 정리하려면 props외에 값을 전달할수있는 방법인것같다.

 

예제) 다크모드 만들기.

context/NameContext.js

import { createContext } from "react";

export const NameContext = createContext("green")

Context를 다른 파일에다가 따로 정의해주고 export로 내보내기 해주었다. 여기서NameContext는 사이트타이틀로만 쓸값이기 때문에 다크모드만드는거랑 크게 상관은 없다.. 그냥 써보는것에 의의를 두는걸로,,

App.js

import { useState } from 'react';
import './App.css';
import Footer from './components/Footer';
import Header from './components/Header';
import Main from './components/Main';
import { NameContext } from './context/NameContext';

function App() {
  const [isDark,setIsDark] = useState(false);
  return (
    <NameContext.Provider value="colazoa">
      <div className="App">
        <Header isDark={isDark}/>
        <Main isDark={isDark}/>
        <Footer isDark={isDark} setIsDark={setIsDark}/>
      </div>
    </NameContext.Provider>
  );
}

export default App;

App에서는 Cotext를 provide컴포넌트로 사용하고 값을 "colazoa"로 바꿔줬다 이 Context값은태그 내부에있는 컴포넌트들에서 사용이 가능하다.

상태값으로 isDark를 만들고 false를 줬다 각각의 컴포넌트에 props로 전달해서 조건으로 스타일을 줄것이다.

Header.js

import React, { useContext } from 'react'
import { NameContext } from '../context/NameContext'

const Header = ({isDark})=>{
    const siteName = useContext(NameContext)
    return(
        <div className='header'style={{
            backgroundColor: isDark? '#222':null,
            color: isDark ? '#fff':'#222'
        }}>
            <h1>{siteName}</h1>
            <ul>
                <li>menu1</li>
                <li>menu2</li>
                <li>menu3</li>
            </ul>
        </div>
    )
}

export default  Header

Header에서 Context를 다시 불러오고 Context값을 siteName에 저장해준다. (app에서 바꾼 value인 colazoa가 들어감)

div 에서 스타일을 주는데 isDark가 true이면 배경은 어두운색으로 바뀌고 글자는 밝은색이된다 false면 반대.

div에 스타일을주면 div안에있는 태그들도 전부 적용된다

Main.js

import React from 'react';

const Main = ({isDark}) => {
    return (
        <div className='main' style={{
            backgroundColor: isDark? '#222':null,
            color: isDark ? '#fff':'#222'
        }}>
            메인페이지입니다
        </div>
    );
};

export default Main;

Main에서도 isDark 를 받아서 조건으로 스타일을 적용시킨다.

Footer.js

import React from 'react';

const Footer = ({isDark,setIsDark}) => {
    const darkToggle = ()=>{
        setIsDark(!isDark);
    }
    return (
        // isDark가 true면 배경색을 #222
        //글자색은 #fff색으로 변경
        <div className='footer' style={{
            backgroundColor:isDark? "#222":null,
            color: isDark ? "#fff":"#222"
        }}>
            <button onClick={darkToggle}>다크모드</button>
        </div>
    );
};

export default Footer;

Footer에서는 isDark상태값과 상태값을 변경시킬함수를 같이받고 

버튼을 클릭하면 isDark를 true로 토글시켜주는 함수를 만들어서 버튼에 넣어준다 . 그러면 버튼을 클릭할때마다 다크모드 전환이 토글된다.

 

자바스크립트 모듈 내보내기 가져오기

1.export 내보내기 import 가져오기
        1)복수의 함수가 있는 라이브러리 형태 export   ---> import 컴포넌트이름
        2)개체가 하나만 선언되어있는모듈 export default  ---->import {컴포넌트이름}
           *export default 개체 하나만 선언된 모듈 

export defaultfunction Header( ){
	const addUser = ( ) =>{ }
	return(
		<div></div>
	)
}



반응형
반응형

Hooks ) 상태 관리를 할 수 있는 useState, 그리고 렌더링 직후 작업을 설정하는 useEffect 등의 기능 등을 제공하여 기존의 함수형 컴포넌트에서 할 수 없었던 다양한 작업을 할 수 있게 해 줍니다.

 

용어정리)                
상태 state         //props도 일종의 state로 취급된다
렌더 : 함수가 다시 호출돼서 화면을 그려줌 //state가 변경될 때 /props가 변경될 때
러프 Ref ref : 컴포넌트의 전 생애주기(마운트~언마운트) 동안 유지됨 

미운트/언마운트
컴포넌트가 보이게 되는걸 마운트 보이지 않게 되는걸 원마운트 

컴포넌트가 리렌더링 되어도 언마운트 되기 전까지 유지됨
state가 변경 => 렌더링이 발생 => 컴포넌트
내부변수들 초기화가 됨(Ref값은 유지됨)
Ref가 변경 => 렌더링 X => 변수들의 값 유지,

useState( ) 
useRef( )


const [text, setText] = useState("초기값")
const ref = useRef(value)

 

Ref

1) Ref생성
useRef(value) 훅 함수를 사용하면 { current: value } 객체를 리턴 
구문 > const ref = useRef(value);  { current:value }

2) Ref값 변경

current키로 값에 접근
ref.current = "green";  {current:"green"}

3) Ref 특징

ref  값은 컴포넌트의 전 생애주기(마운트~원마운트)동안 유지됨
컴포넌트가 리렌더링 되어도 언마운트 되기 전까지 유지됨
state가 변경 => 렌더링이 발생 => 컴포넌트 내부 변수들은 초기화가 됨 (Ref값은 유지됨)
Ref가 변경 => 렌더링 X => 변수들의 값 변하지 않음. 

4) Ref용도
저장공간)

값이 변경되어도 렌더링이 일어나지 않으며

렌더링이 되어도 초기화되지 않고 값이 유지되는 저장공간

 

DOM요소의 접근: document.querySelector( ) 같은 역할을 해 줌

const nameInput = useRef( ); //nameInput이라는 변수명에 useRef객체를 만듦
nameInput.current.focus( ); //nameInput.input태그 에 포커스해줌

<input ref={nameinput}> //ref로 nameInput에 가리키는 Dom요소를 저장함

DomFefSample.js

import React, { useState,useRef } from 'react';

const DomFefSample = () => {
    const [name,setName] = useState("");
    const inputRef = useRef();
    const onReset = ()=>{
        setName("")
        // document.querySelector('#inputname').focus();
        inputRef.current.focus();  //Reset 을 누르면 inputRef.current의위치로 포커스를 옮겨줌
    }
    return (
        <div>
            <input name="name" value={name} id="inputname" ref={inputRef}
            onChange={(e)=>setName(e.target.value)}/>
            <button onClick={onReset}>초기화 클릭</button>
        </div>
    );
};

export default DomFefSample;

자바스크립트처럼 Dom요소를 잡아서 focus를 실행시킬 수 있다.

초기화를 누르면 input으로 포커스가 옮겨짐

 

 

RefSample.js

import React, { useState,useRef } from "react";

function RefSample(){
    //state생성
    const [stateNum,setStateNum] = useState(0);
    // ref생성
    const refNum = useRef(0) //{current: 0}
    //변수 생성
    let varNum=0;
    //변수수정함수 구현
    const addLet = ()=>{
        varNum = varNum+1
        console.log(varNum)
    }  
    //ref수정함수구현
    const addRef = () =>{
        refNum.current = refNum.current+1
    }
    //setState호출 함수 구현
    const addState = () =>{
        setStateNum(stateNum+1);
    }
    return(
        <div>
            <p>State :{stateNum}</p>
            <p>Ref :{refNum.current}</p>
            <p>let :{varNum}</p>
            <button onClick={addState}>State+1</button>
            <button onClick={addRef}>Ref+1</button>
            <button onClick={addLet}>let+1</button>
        </div>
    )
        //state가 바뀔때마다 리렌더링됨 렌더링될때 ref값은 유지 변수값은초기화됨
}

export default RefSample;

RefSample

이 코드를 실행시켜 보면

state가 변경 => 렌더링이 발생 => 컴포넌트 내부 변수들은 초기화가 됨 (Ref값은 유지됨)
Ref가 변경 => 렌더링 X => 변수들의 값 변하지 않음. 

이 내용이 더 쉽게 이해된다.

 

사용해보면 Ref가 더해질 때는 화면에 아무 변화도 일어나지 않고 let변수를 더할 때도 마찬가지이다

let변수는 콘솔창에서 더해지는 걸 확인할 수 있다

let 버튼을 6번 클릭하고 State를 1번 클릭해주고 다시 let을 클릭하면 let 값이 초기화된 걸 확인할 수 있다.

렌더링이 발생 => 컴포넌트 내부 변수들은 초기화가 됨 (Ref값은 유지됨)

Ref를 먼저 5번 클릭했을 때에는 화면에 변화가 없다

Ref가 변경될 때는 렌더링 하지 않기 때문에. 변수도 초기화되지 않는다

State를 눌러주면 Ref값이 같이 렌더링 되면서 화면에 5가 나타나고 변수는 렌더링되면서 초기화된다.

 

이걸 말로 풀어서 할려니까 설명하기가 쉽지 않은데 코드를 직접 실행시켜서 해보면 빠르게 이해가 된다.

 

useEffect

마운트 : 나타남
업데이트 : 리렌더링
원마운트 : 사라짐

useEffect를 사용하여

 

마운트/언마운트/업데이트 시 실행할 작업 설정하기
이 함수를 사용하면 처음화면이 나타날 때 특정 작업을 할 수 있음
리렌더링 될 때마다 특정 작업도 할 수 있음

1) useEffect 구문
useEffect(콜백함수)

*렌더링 될 때마다 실행

useEffect( ( )=>{ 
  //실행할 코드
})

*화면에 첫 렌더링 될 때 실행 * value값이 변경될 때만 실행
useEffect( ( )=>{ 
    //실행할 코드
}, [ value ])

*화면에 첫 렌더링 될 때 만 실행
ex> useEffect( ( )=>{ 
   //실행할 코드
}, [ ])

*마운트됐을 때 실행 , 언마운트 됐을때 실행

useEffect( ( )=>{
      //마운트됐을때 실행코드
	return ( ) =>{
     //언마운트 됐을때 실행
	}
}, [ ] )

예제

Timer.js

import React,{useEffect} from 'react';

const Timer = () => {
    useEffect(()=>{
    	//마운트되었을때 실행
        const timer = setInterval(()=>{
            console.log('타이머돌아가는중')
        },1000);

        //클린업 함수 //언마운트되면 실행
        return()=>{
            clearInterval(timer);
            console.log('타이머가 종료되었습니다.')
        }
    },[]) // [] == 렌더링될때 한번만실행
    return (
        <div>
           <span>타이머를 시작합니다. 콘솔봐주세요</span> 
        </div>
    );
};

export default Timer;

App2.js

import React, { useState,useRef } from "react";
import Timer from "./componetnts/Timer"
function App2(){
    const [showtimer,setshowTimer] = useState(false);
    return(
        <div>
            {showtimer && <Timer/>} //showtimer 가 true일때 <Timer>를 마운트함
            <button onClick={()=>setshowTimer(!showtimer)}>타이머보이기/안보이기</button> 
        </div>
    )
        //state가 바뀔때마다 리렌더링됨 렌더링될때 ref값은 유지 변수값은초기화됨
}
export default App2;

버튼을 클릭할 때 showtimer를 토글 시켜줘서 Timer컴포넌트를 마운트 원마운트 시켜준다. 그러면 TimeruseEffect가 실행되면서 마운트 되었을 때의 구문과 언 마운트될 때의 구문이 실행되게 된다.

 

오늘 갑자기 새로운 걸 좀 많이 배워서 머리가 복잡한데 최대한 정리해보려고 하긴 했는데 뭔가 어수선하게 정리된 느낌이 없잖아 있는 것 같다.. 그래도 또 코드 실행할 수 있게 붙이는 걸 배워서 넣어놨으니 실행해보면서 하면 이해하기

한결 수월하겠지? 내일도  또 리액트한테 조져지러 갈 생각을 하니 정신이 아득하다..

반응형
반응형

오늘도 배운 내용이 참 많지만 일단 비교적 간단했던 style을  적용하는 방법과

각 부분부분 모듈로 나누는 게 어떤 건지 쉽게 정리해보자.

 

React style 적용

1) inline styling

html태그 안에 직접 입력하는 방법
style  =  {  }   json형식(객체)으로속성명 : 속성값으로 입력
속성명 css(-) 대신 camel케이스 방식을 사용  font-size  => fontSize 

 

inline style

<div>
	<h2 style = {{color:"red",fontSize:"24px"}}></h2>
</div>

 

2) JavaScript object

 

객체형식으로 컴포넌트 내에 변수를 선언 스타일을 사용하는 태그에 style = { 변수명 }

object style

function App() {
  const style={
    fontSize :"24px",
    textDecoration:'underline',
    color:'blue'
  }
  return (
    <div className="App">
      <h2 style={{color:"turquoise",backgroundColor:"black"}}>화요일</h2>
      <p style={style}>안녕하세요 오늘은 리액트 스타일을 배워보겟습니다.</p>
    </div>
  );
}

inline,object 방식으로 스타일적용

3) css파일작성

Style.css

*{margin: 0; padding: 0; box-sizing: border-box;}
li{list-style: none;}
body{
    font-family: '나눔고딕','맑은고딕',sans-serif
}

이렇게 일반 css를 작성한 다음에 

import './style.css'

function App() {
  const style={
    fontSize :"24px",
    textDecoration:'underline',
    color:'blue'
  }
  return (
    <div className="App">
      <h2 style={{color:"turquoise",backgroundColor:"black"}}>화요일</h2>
      <p style={style}>안녕하세요 오늘은 리액트 스타일을 배워보겟습니다.</p>
    </div>
  );
}

css를 적용시켜줄 컴포넌트에서 import 해주면 사용할 수 있다.

 

React배열값 출력

변수로 배열을 넣으면 출력해 줌 (출력은 되지만 콘솔에 에러가 뜬다)

 

App.js

function App() {
  const pArr = [
    <p>안녕하세요1</p>,
    <p>안녕하세요2</p>,
    <p>안녕하세요3</p>
  ]

  return (
    <div className="App">
      {pArr}
    </div>
  );
}

배열 출력 결과
배열을 출력한후 콘솔

콘솔에서 key를 가지고 있어야 한다고 알려준다

  const pArr = [
    <p key={1}>안녕하세요1</p>,
    <p key={2}>안녕하세요2</p>,
    <p key={3}>안녕하세요3</p>
  ]

배열에 키를 넣어주고 변수를 사용하면 콘솔에 에러가 뜨지 않는다.

 

컴포넌트예제

컴포넌트는 페이지를 각 부분 부분으로 조각내어서 필요할 때마다 재사용할 수 있고

유지 보수를 간편하게 해 준다.

지금 이 코드는 하나의 부분으로 이루어져 있지만 헤더와 메인 푸터 부분으로 쪼개어서 사용할 수 있고

쪼갠 부분은 다른 페이지에서도 똑같이 사용할 수 있다.

 

App.js 해체 전

function App() {
  return (
    <div className="App">
      <header>
        <h1>Green</h1>
        <ul>
          <li>menu1</li>
          <li>menu2</li>
          <li>menu3</li>
          <li>menu4</li>
        </ul>
      </header>
      <main>
        <h2>main title</h2>
        <p>main desc</p>
      </main>
      <footer>
        <h1>green</h1>
        <address>울산시 남구 삼산동 삼산중로 100번길 그린컴퓨터아카데미</address>
      </footer>
    </div>
  );
}

해체전 화면

학원에서 만든 예제라 학원주소가 들어가긴 하는데뭐 상관없겠지..? 일단 블러

 

App.js 해체 후

function Header(){
  return(
    <header>
      <h1>Green</h1>
      <ul>
        <li>menu1</li>
        <li>menu2</li>
        <li>menu3</li>
        <li>menu4</li>
      </ul>
    </header>
  )
}
function Main(){
  return(
    <main>
      <h2>main title</h2>
      <p>main desc</p>
    </main>
  )
}
function Footer(){
  return(
    <footer>
      <h1>green</h1>
      <address>울산시 남구 삼산동 삼산중로 100번길 그린컴퓨터아카데미</address>
    </footer>
  )
}


function App() {
  return (
    <div className="App">
      <Header />
      <Main/>
      <Footer />
    </div>
  );
}

이렇게 각 부분을 컴포넌트로 만들고 불러서 사용하면 필요할 때 다시 사용할 수도 있고 코드도 간결해진다.

(컴포넌트 왔다 갔다 하는 건 불편하지만..)

여기서도 필요하다면 더 나눌 수 있는데 헤더에 있는 ul을 nav라는 컴포넌트로 묶어서 따로 빼주고 헤더에서 nav컴포넌트를 사용하면 똑같이 나온다.

 

App.js 쪼개고 쪼개고..(원자단위까지..)

function Header(props){
  const {list} = props
  return(
    <header>
      <h1>Green</h1>
      <Nav lists={list}/>
    </header>
  )
}
function Main(){
  return(
    <main>
      <h2>main title</h2>
      <p>main desc</p>
    </main>
  )
}
function Footer(){
  return(
    <footer>
      <h1>green</h1>
      <address>울산시 남구 삼산동 삼산중로 100번길 그린컴퓨터아카데미</address>
    </footer>
  )
}
function Nav(props){
  const {lists} = props
  return(
    <ul>
      {lists.map(li=><li key={li.id}>{li.title}</li>)}
    </ul>
  )
}


function App() {
  const lists = [
    {id:1,title:'menu1'},
    {id:2,title:'menu1'},
    {id:3,title:'menu1'},
    {id:4,title:'menu1'},
  ] //App의 배열을 Nav컴포넌트에 전달하는방법
  return (
    <div className="App">
      <Header list={lists}/>
      <Main/>
      <Footer />
    </div>
  );
}

li부분은 배열로 만들고 map 메서드를 써서 html형태로 리턴 시켜줬다.

배열로 만들어서 Header로 전달 Header에서 받아서 Nav로 전달 Nav에서 배열을. map메서드로

형태를 변경해서 출력

컴포넌트로 모두나눠도 같은화면이 나온다.

컴포넌트 파일나누기

이제 App.js안에 나눠놨던 컴포넌트들을 각각의 파일로 나눠준다. (다른 파일에서도 각각 불러 쓰기 편하게)

 

Header.js

import React from 'react'
import Nav from './Nav'

function Header(props){
    const {list} = props
    return(
      <header>
        <h1>Green</h1>
        <Nav lists={list}/>
      </header>
    )
}

export default Header

Nav.js

import React from 'react'

function Nav(props){
    const {lists} = props
    return(
      <ul>
        {lists.map(li=><li key={li.id}>{li.title}</li>)}
      </ul>
    )
  }

export default Nav

Main.js

import React from "react";

function Main(){
    return(
      <main>
        <h2>main title</h2>
        <p>main desc</p>
      </main>
    )
  }
  
export default Main

Footer.js

import React from 'react'

function Footer(){
    return(
      <footer>
        <h1>green</h1>
        <address>울산시 남구 삼산동 삼산중로 100번길 그린컴퓨터아카데미</address>
      </footer>
    )
}

export default Footer

App.js

import Header from './componetnts/Header';
import Main from './componetnts/Main';
import Footer from './componetnts/Footer';


function App() {
  const lists = [
    {id:1,title:'menu1'},
    {id:2,title:'menu1'},
    {id:3,title:'menu1'},
    {id:4,title:'menu1'},
  ] //App의 배열을 Nav컴포넌트에 전달하는방법
  return (
    <div className="App">
      <Header list={lists}/>
      <Main/>
      <Footer />
    </div>
  );
}

 

컴포넌트를 나눈다는 개념이 어떤 건지 어렴풋이 알 것 같은 느낌이었는데 이렇게 직접 하나의 파일을

각 부분별 기능별로 쪼개서 컴포넌트를 만들고 사용하면 관리하기도 편하고 코드도 보기에

좀 더 간결해 보이는 효과가 있는 것 같다. 헤더를 여러 개 넣고 싶으면 헤더 컴포넌트만 여러 번 써주면 되고

헤더컴포넌트 내용만 바꿔도 헤더컴포넌트를 사용하는 모든 페이지에서 똑같이 변경된다는 점이

관리하는 관점에서는 큰 장점으로 보인다. 지금은 그냥 만들고 끝이지만 실제 현업에서는 만든 파일들을

주기적으로 유지. 보수해줘야 하니까 각각의 파일에 들어가서 변경해 주는걸

한 개의 컴포넌트에서 처리할 수 있게 해 주는 게 압도적으로 일을 줄여주겠지??

반응형
반응형

todolist를 클래스형 컴포넌트로 만들어보겠다  바로 ㄱㄱ

 

우선은 html구조부터만들어준다

 

Classiteration.js

import React,{Component} from 'react'
class Classiteration2 extends Component{
    render(){
        return(
            <div>
                <input/>
                <button>추가</button>
                <ul>
                    <li>할일1<button>삭제</button></li>
                </ul>
            </div>
        )
    }
}
export default Classiteration2

 

 

기본화면구성

여기서부터는 함수형에서 했던 것과 똑같이 필요한 기능의 함수들을 하나씩 만들어서 넣어주고

상태값도 만들어서 관리해주면 된다.

class Classiteration2 extends Component{
    state={
        list:[],
        input:""
    }
    Change = (e)=> {
        this.setState({
            ...this.state,
            input:e.target.value
        })
    }
    render(){
        const{list,input} = this.state
        return(
            <div>
                <input value={input} onChange={this.Change}/>
                <button>추가</button>
                <ul>
                    <li>할일1<button>삭제</button></li>
                </ul>
            </div>
        )
    }
}

constructor를 쓰지 않고 state로 상태값객체를 만들어주고 render메서드 안에서 변수에 할당해서

input태그 안에 넣어주고 input태그 값이 변할 때 실행될 메서드  Change도 만들어줬다.

클래스에서는 메서드이기 때문에 this. 메서드로 접근해서 사용한다

Change 함수는 state에 원래 들어있던 값들을 다시 저장해주고 input부분만 입력되고 있는

부분들이 들어갈 수 있게 써줬다.

class Classiteration2 extends Component{
    state={
        list:[],
        input:""
    }
    Change = (e)=> {
        this.setState({
            ...this.state,
            input:e.target.value
        })
    }
    onAdd = ()=>{
       const nextlist = [...this.state.list,{id:this.state.list.length,text:this.state.input}] 
        this.setState({
            list:nextlist,
            input:""
        })
    }

    render(){
        const{list,input} = this.state
        let lists = list.map(li=><li key={li.id}>{li.text}<button>삭제</button></li>)
        return(
            <div>
                <input value={input} onChange={this.Change}/>
                <button onClick={this.onAdd}>추가</button>
                <ul>
                    <li>할일1<button>삭제</button></li>
                    {lists}
                </ul>
            </div>
        )
    }
}

 

여기서는 버튼을 누를 때 setState로 리스트를 추가해주고 input은 비운다 추가된 list값은 

reder 메서드 안에서 map함수로 html태그형태로 바꿔주고 lists안에 저장한다.

이제 삭제하기만 하면 끝~

class Classiteration2 extends Component{
    state={
        list:[],
        input:""
    }
    Change = (e)=> {
        this.setState({
            ...this.state,
            input:e.target.value
        })
    }
    onAdd = ()=>{
       const nextlist = [...this.state.list,{id:this.state.list.length,text:this.state.input}] 
        this.setState({
            list:nextlist,
            input:""
        })
    }
    del=(id)=>{
        const nextlist = this.state.list.filter(li=>li.id !== id)
        this.setState({
            ...this.state,
            list:nextlist
        })
    }

    render(){
        const{list,input} = this.state
        let lists = list.map(li=><li key={li.id}>{li.text}<button onClick={()=>this.del(li.id)}>삭제</button></li>)
        return(
            <div>
                <input value={input} onChange={this.Change}/>
                <button onClick={this.onAdd}>추가</button>
                <ul>
                    <li>할일1<button>삭제</button></li>
                    {lists}
                </ul>
            </div>
        )
    }
}

 

함수형에서 쓰던 거랑 똑같이 id를 받아서 list의 id와 다른 요소들만 nextlist에 저장해서 setState로 넣어준다.

 

사실 클래스형이라고 함수형과 크게 다르지 않다. 메서드를 사용할 때 앞에 this만 좀 잘 붙여주고 

상태를 변경하는 함수가  setState라는 것만 잘 기억하고 쓰면 크게 어렵지 않게 바꿀 수 있을 것 같다.

반응형
반응형

클래스는 객체를 생성하기 위한 템플릿이다.

데이터와 데이터를 조작하는 코드를 하나로 묶어준다.

객체를 만들어주는 일종의 붕어빵 틀이라고 생각하면 이해하기 쉽다.

붕어빵 틀에 각종 재료를 넣고 구워내면 내용은 다르지만 같은 모양을 가진 붕어빵이 나오듯이

클래스에 맞춰서 코드를 써주면 표준화된 형태의 결과를 만들 수 있다.

 

클래스와 객체

클래스) 객체를 만들기 위한 도구 템플릿

class Car { //car라는 객체를 만들어내기위한 틀
	constructor(color,speed){
    		this.color = color;
    		this.wheels = 4;
    		this.speed = speed;
    	}
    	drive(){ // 메서드
            console.log(`차가 ${this.speed}로 달립니다`)
            // 클래스에서 자기자신의 속성에접근할때는 this를 이용해서 접근한다
           }
}

 

 

클래스 없이 객체를 만든다면

const car1 = {
    color : "green",
    wheels : 4
}
const car2 = {
    color:"red",
    wheels:4
}

클래스가 있다면

const car3 = new Car("blue",60);
car3.drive() //클래스의 메서드호출

클래스가 있다면 같은 방법으로 여러 개의 객체를 쉽게 만들 수 있다.

 

클래스 Static 키워드

class Article{
    constructor(articleNumber){
        this.articleNumber = articleNumber
    }
    static publisher = "Green Coding";
    static printPublisher(){
        console.log(this.articleNumber)
    }
}

메소드의 위치

하나는 아까 위에서 만들었던 Car class이고 아래는 이번에 만든 Article class이다 

Car class의 메서드는 [[Prototype]] 안에 있지만 

Article class의 static 키워드를 사용한 메서드는  constructor 안에 들어있는 걸 볼 수 있다.

 

그래서 일반 클래스와 메서드를 사용하는 방법이 다르다.

일반메서드에서는 오브젝트. 메서드로 접근한다  car.drive( ) 

스태틱을 사용한 변수나, 메서드는 클래스이름을 사용해서 접근해야 한다  클래스. 메서드

console.log(Article.publisher)
Article.printPublisher() 
//sttic은 클래스이름으로 접근한다. //일반메서드는 오브젝트.메서드로 접근

 

오버라이딩

상속관계에 있는 부모 클래스에서 이미 정의된 메서드를 자식 클래스에서 메서드로 다시 재정의 하는 것.

 

부모가 될 클래스 Shape

class Shape{
    constructor(width,height,color){
        this.width = width;
        this.height = height;
        this.color = color;
    }
    draw(){
        console.log(`${this.color}로 그립니다.`);
    }
    gerArea(){
        return this.width * this.height;
    }
}

Shape를 상속받는 클래스

class Rectangle extends Shape{
    //생성자 오버라이딩
    constructor(width,height,color,name){
        // 부모 생성자  
        super(width,height,color) //super 를 써서 부모요소를 받아옴
        // this.width = width;
        // this.height = height;
        // this.color = color;
        this.name = name
    }
    // 오버라이딩
    draw(){
        super.draw(); // console.log(`${this.color}로 그립니다.`);
        console.log('사각형을 그립니다.')
    }
}
const Ractangle = new Rectangle(20,20,'yellow'); //Rectangle 클래스로 Ractangle객체를 생성 
console.log(Ractangle); //Ractangle 출력
Ractangle.draw(); //Ractangle draw메서드 사용

Rectangle객체가 만들어진모습 draw 메서드가 사용되는 모습

 

객체가 어떤 클래스를 이용해서 만들어졌는지 확인

//instanceof / a instanceof b  / a객체가 b클래스를 이용해서만들엇는지
console.log(Ractangle instanceof Rectangle) //true
console.log(Ractangle instanceof Shape) //true
console.log(Ractangle instanceof Car) //false

객체가 클래스를 이용해만들어졌는지 확인

Ractangle 객체는 Shape클래스를 상속받은 Rectangle클래스를 이용해서 만들어졌기 때문에 Shape클래스와 Rectangle클래스를 둘 다 이용해서 만들었다고 할 수 있다 그래서 true가 결괏값으로 나오게 된다.

 

클래스는 리액트에서 클래스형 컴포넌트를 사용할 때 사용하기 때문에 construdctor 나 this키워드 의 사용법을 잘 모른다면 리액트에서도 이해하기 힘들다.  메서드나 오버라이딩 같은 것들도.. 앞으로도 리액트 수업을 하다가 막히면 종종 자바스크립트 복습을 할 텐데 그때마다 놓치지 말고 꼼꼼하게 정리해 놔야겠다.

반응형
반응형

선생님도 리액트를 가르쳐주시다가 이렇게 부실한 자바스크립트 베이스로는 진도를 나가기가 힘들다고 판단하셨는지 오늘은 자바스크립트의 클래스와 객체에대한 복습을 했다. 오랜만에 들어도 또 새로운 것 같은 느낌...

 

객체 object
물리적으로 존재하거나 추상적으로 생각할 수 있는 것 중에서 속성을
가지고 있고 다른 것과 식별 가능한 것

-> 이름(key)과 값(value)으로 구성된 속성의 집합이다.

ex)

귀여운 고영희씨

이 귀여운 고양이를 객체로 만들어서 나타내보자


고양이 - 객체
속성(property)
cat.name = "고영희"
cat.family = "코리안 숏 헤어"
cat.age = 2
cat.weight = 1000

 

cat = {

    name:"고영희",

    family:"코리안 숏 헤어",

    age:2,

   weight:1000

}

이게 이 고양이라는 객체를 나타내주는 것이다. 

 

메서드(method)  // 속성의 값이 함수인 속성 // 객체에서 사용하는 함수
cat.eat( ) 
cat.sleep( )
cat.play( )

 

이런 식으로 객체에 함수를 넣어서 사용하는 것을 메서드 라고 한다.

cat = {
   name:"고영희",
   family:"코리안 숏 헤어",
   age:2,
   weight:1000,
   eat( ){ 맛있게먹는다},
   sleep( ) { 잔다 },
   play( ){ 움직인다 }
}

 

 

객체를 만드는 방법은 객체 리터럴 문법과 객체 생성자 문법이 있다.

 

객체 리터럴 문법
let cat = { };

 

객체 생성자 문법
let cat = new Object( );

 

또는 객체를 만드는 함수를 만들어서 객체를 만들어줄 수도 있다

function makeUser(name,age){
	return{
       // 단축프로퍼티 키의문자와 변수명이같을때 줄일수잇음
       name, // name: name,
       age   // age:age
    }
}

let user = makeUser("green",30)

name과 age를 파라미터로 받아서 객체로 리턴해주는 함수이다.  

 

객체의 키값에 변수를 넣는 방법 

let keyname = "weight"

let cat = {
	name:"고영희",
 	age:3,
	[ keyname ] : 2000
}

key값에 변수를 넣는법

 

객체의 속성값에 접근하는 방법

객체의 value 값은 객체이름과 key값으로 접근할 수 있다

cat 객체의 이름에 접근하려면 

1.  cat.name
2.  cat['name']

3.  let aa="name"
 	cat[aa]

이렇게 3가지 방법으로 접근할 수 있다 

1.을 찍고 키값으로 접근하거나

2  대괄호 안에 키값을 넣어서 접근하거나 

3  대괄호안에 변수를 넣어서 접근할 수 있다. (변수를 사용할 때는 대괄호로만 접근할 수 있다.)

 

 

 

선언한 객체에 값을 추가하는 방법

let keyname = "weight"

let cat = {
	name:"고영희",
 	age:3,
	[ keyname ] : 2000
}

cat.age = 4
cat.favorite = "츄르"

 

이렇게 객체를 선언한 후에 객체에 키값으로 접근해서 값을 수정하거나

객체에 없는 키값에 값을 넣어서 저장할 수도 있다.

객체의 값 수정과 추가

 

객체에 키값이 있는지 확인

let user = {
	name:"colazoa",
    age: 50/2
}

console.log("name" in user) // true
console.log("weight" in user) // false

콘솔에서 확인

객체 반복문 (객체의 모든 키값을 순회)

for(key in user){                 //user객체의 키를 하나씩 key변수에 대입하는 반복
       console.log(key)
       console.log(user[key]);
}

객체의 키값을 콘솔에 로그 해주고.

객체에 키값으로 접근해서 value를 콘솔에 출력해 준다.

 

객체의 복사

객체의 변수에는 객체가 그대로 저장되는 것이 아니라, 객체가 저장되어 있는 '메모리 주소’인 객체에 대한 '참조 값’이 저장 되게 된다.

let user1={
	name="colazoa"
}

예를 들어 이렇게 한 개의 객체를 생성하면 객체는 메모리 내 어딘가에 저장되고,

변수 user1에는 객체를  '참조’ 할 수 있는 값이 저장되게 된다.객체가 할당된 변수를 복사할 때에는

객체의 참조 값이 복사되고 객체자체는 복사되지 않는다.

객체는 하나이고 참조하는 변수가 2개가 되는 것이다.

 

하나의 객체를 참조하는 두 변수 user1 , seconduser

let user1={
	name="colazoa"
}
let seconduser = user1

https://ko.javascript.info/object-copy

이런 느낌이 되는 것이다. 

이 상태에서 user1의 name을 바꾸면 같은 객체를 참조하는 seconduser에서의 name 값도 바뀌는 것이다.

 

콘솔 창에서 쳐보면 알 수 있듯이 처음에 colazoa라는 name을 가진 user1을 seconduser에 복사했다

user1과 seconduser 모두  name 값으로 "colazoa"를 가지고 있다.

user1에서 name을 Ciderzoa로 수정하자 같은 객체를 참조하는

seconduser도 name값이 바뀐 것을 확인할 수 있다.

 

이런 문제를 방지하기 위해서는 복사할 때 똑같은 객체를 다른 메모리에 하나 더 저장해주어야 한다.

 

같은 객체를 생성하는 방법

//객체 반복문을 이용한 방법
let seconduser = {}
for(let key in user){
	seconduser[key] = student1[key]
}  

//스프레드 구문을이용한방법
let seconduser = {
	...user1,
}

새로운 객체를 만들고 객체 안에 복사할 객체의 속성들을

어떤 방법으로든 똑같이 넣어서 같은 구조의 객체를 만들어주면 된다. 이렇게 해주면 한쪽의 값이 수정되어도 다른 쪽에서 참조하거나 하는 영향이 없기 때문에 각각 사용이 가능하다.

 

자바스크립트를 배웠을 때에는 뛰어넘었던 내용들도 있는데 아무래도 리액트에 객체형태가 자주 나오다 보니 객체를 제대로 다룰 수 있어야 리액트를 사용할 때 큰 문제가 없는 듯하다.. 

반응형
반응형

이런모양의 todolist를 만들어줄것이다 +버튼을 누르면 밑에 내용이추가되고 ,

내용옆에있는 x를 누르면 내용이삭제되도록 변경해줄예정.

 

Todolist.js (easy.ver) 완성

import React,{useState} from 'react'

const Todolist = ()=>{
    const[list,setlist] = useState([]);
    const[input,setinput] = useState("");

    // 인풋에 입력할때 값을 바꿔줄 함수
    const onChange = (e)=>{
        setinput(e.target.value)
    }
    // +버튼을 클릭하면 리스트에 추가
    const onAddTodo = ()=>{
        const nextlist=[
            ...list,
            {id:list.length+1,text:input}
        ]
        setlist(nextlist); //새로만든 리스트를 list에 적용
        setinput("")//input비워주기
    }
    // // X를 클릭하면 삭제되게
    const delTodoLists = (id) => {
        const nextlist = list.filter(list=> list.id !== id)
        setlist(nextlist)
    }    
    return(
        <div className='todo'>
            <div className='header'>
                <h2>To do List</h2>
                <div>
                    <input onChange={onChange} value={input} />
                    <button onClick={onAddTodo}>+</button>
                </div>
            </div>
            <div>
                <ul className='todoLists'>
                    {list.map(todo=><li key={todo.id}>{todo.text}
                    <span onClick={()=>{delTodoLists(todo.id)}}>X</span></li>)}
                </ul>
            </div>
        </div>
    )
}

export default Todolist;

 

좀 복잡하긴한데 배웠던 내용들을 활용한거라서 천천히 읽으면 그렇게 어렵지는 않다.

App.js에 import 해주고 렌더링하면 이런화면이 나오게된다 css는 아직... 제일마지막에 해보겠다
코드도 하나씩 천천히 다시 써보겠다.

 

우선은 안에 함수와 상태값들 다빼고 html구조만 먼저 잡아보자

const Todolist = ()=>{
    return(
        <div className='todo'>
            <div className='header'>
                <h2>To do List</h2>
                <div>
                    <input/>
                    <button>+</button>
                </div>
            </div>
            <div>
                <ul className='todoLists'>
                    <li>할일이없어요<span>X</span></li>
                </ul>
            </div>
        </div>
    )
}

이제 저기에서 input에 값을 입력하면 input.value가 바뀌고 +버튼을 클릭하면

아래에있는 <ul> 에 <li>가 추가되어서 내용이 들어가게 해줄것이다

 

input에 입력될때 input.value변경

const Todolist = ()=>{
    const [input,setInput] = useState(""); //인풋태그의 초기값은 공백
    const inputchange = (e)=>{ // e.target 는 해당함수를 실행시키는요소 == input
        setInput(e.target.value) //e.target.value == input.value
    }
    return(
        <div className='todo'>
            <div className='header'>
                <h2>To do List</h2>
                <div>
                    <input value={input} onChange={inputchange}/>
                    <button>+</button>
                </div>
            </div>
            <div>
                <ul className='todoLists'>
                    <li>할일이없어요<span>X</span></li>
                </ul>
            </div>
        </div>
    )
}

 

 

+버튼을 누를때 input의 내용이 list에 추가되게 해주고 ul에 보여주기

list 는 키넘버와 내용을 가진 객체를 배열로 저장 [ {key:1,text:할일1 }, {key:2,text:할일2}] 

    const[list,setlist] = useState([])
    const addlist=()=>{
        const newlist = [...list,{key:list.length+1,text:input}]
        setlist(newlist);
        setInput(""); 
    } 리스트값의 상태를 관리할 변수를 useState로 만들어주고(값이 여러개들어가기때문에 배열로만들어준다)

addlist함수가 실행되면 newlist 라는 변수를 만들게되고. 이 변수에는 기존에 리스트에있던값들과

새로운 객체를 추가 해서 저장한다. 새로운 list가저장된변수를 setlist에 넣어서 list상태를 변환시켜준다.

이제 이렇게 만들어진 list 를 렌더링 해줘야한다. 

                <ul className='todoLists'>
                    <li>할일이없어요<span>X</span></li>
                    {list.map(todo=><li key={todo.id}> {todo.text} <span>X</span> </li>)}
                </ul>

list는 배열이기 때문에 배열메소드인 map을 이용해서 새로 html형식으로 만들어서 리턴시켜준다. li에 key가 있는데 key는 요소를 파악하기 위해서 달아주는 거라고 보면된다 key가없다면 변경이 필요하지않은 요소도 같이 변경이 일어날수도있기때문에 달아주는것이다.

 

현재까지의 전체코드

import React,{useState} from 'react'

const Todolist = ()=>{
    const [input,setInput] = useState(""); //인풋태그의 초기값은 공백
    const[list,setlist] = useState([]);
    
    const inputchange = (e)=>{ // e.target 는 해당함수를 실행시키는요소 == input
        setInput(e.target.value) //e.target.value == input.value
    }

    const addlist=()=>{
        const newlist = [...list,
            {id:list.length+1,text:input}
        ]
        setlist(newlist);
        setInput(""); 
    }
    console.log(list)
    return(
        <div className='todo'>
            <div className='header'>
                <h2>To do List</h2>
                <div>
                    <input value={input} onChange={inputchange}/>
                    <button onClick={addlist}>+</button>
                </div>
            </div>
            <div>
                <ul className='todoLists'>
                    {list.map(todo=><li key={todo.id}> {todo.text} <span>X</span> </li>)}
                </ul>
            </div>
        </div>
    )
}

export default Todolist;

 

이제 추가는 잘되는데 삭제는 할수가없다  todolist니까 하고나면 삭제를 해줘야한다. 

 

삭제기능 

    const dellist = (id)=>{
        const newlist=list.filter(list=> list.id !== id)
        setlist(newlist)
    }

id를 받아서 클릭한리스트의 id와 같으면 filter에서 false 가 나와서 걸러지게되고

그외에 list들은 newlist에 저장된다.

setlist에 바뀐 newlist를 넣어서 state를 변경해준다.

{list.map(todo=><li key={todo.id}> {todo.text} 
<span onClick={()=>dellist(todo.id)}>X</span> </li>)}

 태그안에서 파라미터를 주기위해서 함수를  dellist를 실행시키는 화살표함수를 하나 만들어서 사용했다.

이렇게 사용하면 dellist함수에 todo.id를 파라미터로 보내줄수있다.

 

완성코드

import React,{useState} from 'react'

const Todolist = ()=>{
    const [input,setInput] = useState(""); //인풋태그의 초기값은 공백
    const[list,setlist] = useState([]);
    
    const inputchange = (e)=>{ // e.target 는 해당함수를 실행시키는요소 == input
        setInput(e.target.value) //e.target.value == input.value
    }

    const addlist=()=>{
        const newlist = [...list,
            {id:list.length+1,text:input}
        ]
        setlist(newlist);
        setInput(""); 
    }
    const dellist = (id)=>{
        const newlist=list.filter(list=> list.id !== id)
        setlist(newlist)
    }
    console.log(list)
    return(
        <div className='todo'>
            <div className='header'>
                <h2>To do List</h2>
                <div>
                    <input value={input} onChange={inputchange}/>
                    <button onClick={addlist}>+</button>
                </div>
            </div>
            <div>
                <ul className='todoLists'>
                    {list.map(todo=><li key={todo.id}> {todo.text} 
                    <span onClick={()=>dellist(todo.id)}>X</span> </li>)}
                </ul>
            </div>
        </div>
    )
}

export default Todolist;

여기서 끝나면 좋겠지만... 저번에 말했던것처럼 리액트는 모듈별로 나누고 각각 조립해서사용게 장점이기 때문에 이 컴포넌트를 두개로 나눠야한다....(왜 어째서..ㅠ)

Hard.ver 시작

우선은 입력하는 헤더부분과 입력된값을 보여주는 리스트부분 으로 나눠서 컴포넌트를 만들어준다.

 

Todoheader.js

import React from 'react'

const Todoheader = ()=>{

    return(
        <div className='header'>
            <h2>To do List</h2>
            <div>
                <input />
                <button >+</button>
            </div>
        </div>
    )
}

export default Todoheader

Todolists.js

import React from 'react';

const Todolists = ()=>{
    return(
        <div>
            <ul>
                <li>리액트지옥<span>X</span></li>
            </ul>
        </div>
    )
}

export default Todolists

이렇게 두개의 분리된 컴포넌트를 만들고 

App.js 에서 두개를 렌더 해준다.

App.js

  return (
    <div className="App todo">
      <Todoheader/>
      <Todolists/>
    </div>
  );

아까처럼 결과화면을 볼수있다. 이제 사용할함수들을 만들어주고 적용해주면 된다.

이번에는 App.js에서 상태값과 함수를 만들어 props로 각 컴포넌트에 적용해주겠다.

 

App.js

import './App.css';
import Todoheader from './components/Todoheader';
import Todolists from './components/Todolist';
import { useState } from 'react';

function App() {
  const [todoState,setTodostate] = useState({
    todoLists:[],
    inputText:""
  })
  const Change = (e)=>{
    setTodostate({
      ...todoState, //원래있던 todostate값을 저장
      inputText: e.target.value
    });
  };
  const Addlist=()=>{
    const newtodolist = [
      ...todoState.todoLists,
      {id:todoState.todoLists.length,text:todoState.inputText}
    ]
    setTodostate({
        todoLists:newtodolist,
        inputText:""
    })
  }
  const delTodoLists = (id)=>{
    const newtodolist = todoState.todoLists.filter(list=>list.id !== id);
    setTodostate({
      ...todoState,
      todoLists:newtodolist
    })  
  }
  return (
    <div className="App">
      <Todoheader
      inputText={todoState.inputText} //value로 들어갈값
      Change ={Change}
      Addlist={Addlist}      
      />
      <Todolists
      todoLists = {todoState.todoLists} // li를 만들기위해 리스트값을 받아옴
      deltodolists = {delTodoLists}
      />
    </div>
  );
}

export default App;

좀 쉽게 설명하자면 아까 한파일에서 만들었던걸 이번에는 App.js에서 만든다고 보면된다

조금 다른점은 이번에는 상태값에 input값도 같이 포함되어있다는점.. 그래서 값을 변경해줄때

input값도 잊지말고 같이 설정해주어야한다.

 

나머지는 다 똑같이 쓰던 함수들을 정의하고 Todoheader에 인풋값이 변할때 value값을 바꿔주는 함수와 value 로 들어갈 input 값 버튼을 누를때 list 배열에 input값이 추가되는 함수들을 props로 전송해준다.

 

Todoheader.js

import React from 'react'

const Todoheader = ({inputText,Change,Addlist})=>{

    return(
        <div className='header'>
            <h2>To do List</h2>
            <div>
                <input value={inputText} onChange={Change}/>
                <button onClick={Addlist}>+</button>
            </div>
        </div>
    )
}

export default Todoheader

Todoheader에서는 보내준 props값을 객체 구조분해 할당으로 받아서 필요한 함수와 변수를 넣어준다.

아까 만든부분에서 위에 함수를 정의했던부분과 state값을 만들었던부분만 없어졌다고 보면 편하다.

 

Todolists.js

import React from 'react';

const Todolists = ({todoLists,deltodolists})=>{
    return(
        <div>
            <ul>
                {todoLists.map(list=><li key={list.id}>{list.text}
                <span onClick={()=>deltodolists(list.id)}>X</span></li>)}
            </ul>
        </div>
    )
}

export default Todolists

Todolists에서는 App.js에서 props로 보내준 list값과 deltodolist함수를 이용해서

전처럼 <li>를 만들어서 뿌려주고 

그 안에 deltodolists함수를 넣어줬다. 

기능적으로 똑같이 추가와 삭제가가능하다.

 

 

이제 여기에 css를 넣으면 완성!

 

Todo.css

* { margin: 0; padding: 0; box-sizing: border-box; vertical-align: middle;}
li{
    list-style: none;
    border-bottom: 1px solid #ccc;
}
span{
    float: right;
}
.todo {
    width: 80%;
    max-width: 800px;
    margin: 0 auto;
}
.header {
    background-image: linear-gradient(to right, #9795f0 0%, #fbc8d4 100%);
    height: 140px;
    padding-top: 30px;
}
.header h2 {
    color: #fff;
    font-size: 32px;
}
.header input {
    width: 70%;
    height: 40px;
    border-radius: 20px;
    border: none;
    outline: none;
    padding-left: 30px;
}
.header button {
    height: 40px;
    width: 40px;
    border-radius: 20px;
    border: none;
    outline: none;
    background: #fff;
    color: #fbc8d4;
    font-size: 24px;
    font-weight: bold;
    margin-left: 8px;
}
.todoLists {
    list-style: none;
}
.todoLists li {
    line-height: 40px;
    background-color: #f5f5f5;
    text-align: left;
    padding: 0 20px;
    border-bottom: 1px solid #ccc;
}
.todoLists li button {
    float: right;
    border: none;
    outline: none;
    line-height: 40px;
}

 

이번주까지 배운React 내용들은 주말동안 전부 정리한것같다 확실히 블로그에 이렇게 한줄씩 쓰면서

남한테 설명하는느낌으로 정리를 하다보니 이해도가 좀 더 잘 올라가는것같다. 다음주 리액트도 열심히 정리하자,

네이버 블로그에 써놓은 내용도 조금씩 옮기고,, ㅠ

반응형

+ Recent posts