단순 코드 기록/RE: React

React 기초

일일일코_장민기 2024. 5. 20. 20:42
728x90

React를 거의 몇 달만에 다시 만지면서 기초부터 다시 해보자는 생각에 기록하게 되었다.

 

순서

1. 태그의 대소문자

2. 사용자 정의 태그(컴포넌트)의 위치

3. prop(속성) - props(속성들)

3.1 속성 배열 사용

4. 초기화하지 않은 함수의 일반형과 람다형

5. prop으로 이벤트 전달

6. state

7. useState

8. useState2

9. Create

10. Update

11. delete

 

전체 코드

더보기
import './App.css';
import { useState } from 'react';

//App================================================================================
function App() {

  // 변수
  let content = null;
  let contextControl = null;

  // useState
  const [mode, setMode] = useState("WELCOME");
  const [id, setId] = useState(null);
  const [topics, setTopics] = useState([  { id: 1, title: 'html', body: 'html is...' },
                                          { id: 2, title: 'css', body: 'css is...' },
                                          { id: 3, title: 'js', body: 'js is...' },
                                      ]);
  const [nextId, setNextId] = useState(4);

  // 조건에 따라 state값 변경
  if(mode === "WELCOME"){
    content = <Article title="Welcome" body="Hello, web"></Article>
  };
  if(mode === "READ"){
    let title, body = null;
    for (let index = 0; index < topics.length; index++) {
      let element = topics[index];
      if(element.id === id){
        title = element.title;
        body = element.body;
      }
    }
    content = <Article title={title} body={body}></Article>
    contextControl = <>
      <li><a href={'/update/'+id} onClick={(event) => {event.preventDefault(); setMode("Update");}}>Update</a></li>
      <li><input type='button' value="삭제" onClick={()=>{
                                                          const newTopics = [];
                                                          for (let index = 0; index < topics.length; index++) {
                                                            const element = topics[index];
                                                            if(element.id !== id){
                                                              newTopics.push(element);
                                                            }
                                                          }
                                                          setTopics(newTopics);
                                                          setMode("WELCOME")
                                                          }}>
      </input></li>
    </>
  };
  if(mode === "Create"){
    content = <Create onCreate={(newTitle, newBody) => {
                                                          const newTopic = {id: nextId, title: newTitle, body: newBody}
                                                          const newTopics = [...topics];
                                                          newTopics.push(newTopic);
                                                          setTopics(newTopics);
                                                          setMode("READ");
                                                          setId(nextId);
                                                          setNextId(nextId + 1);
                                                        }
                                }
              ></Create>
  };
  if(mode === "Update"){
    let title, body = null;
    for (let index = 0; index < topics.length; index++) {
      let element = topics[index];
      if(element.id === id){
        title = element.title;
        body = element.body;
      }
    }
    content = <Update title={title} body={body} onUpdate={(updatedTitle, updatedBody) => {
                                                          const updatedTopic = {id: id, title: updatedTitle, body: updatedBody}
                                                          const updatedTopics = [...topics];
                                                          for (let index = 0; index < topics.length; index++) {
                                                            const element = topics[index];
                                                            if(element.id === id){
                                                              updatedTopics[index] = updatedTopic;   // 기존 데이터를 갱신
                                                              break;
                                                            }
                                                          }
                                                          setTopics(updatedTopics);
                                                          setMode("READ");
                                                        }
                                }
              ></Update>
  }

  // 출력
  return (
    <div>
      <Header title="Web" onChangeMode={() => {setMode("WELCOME")}}></Header>
     
      <Nav topics={topics} onChangeMode={(id) => {setMode("READ"); setId(id)}} ></Nav>
     
      {content}

      <ul>
        <li><a href='/create' onClick={(event) => {event.preventDefault(); setMode("Create");}}>Create</a></li>
        {contextControl}
      </ul>
    </div>
  );
}
//App 종료============================================================================

//헤더================================================================================
const Header = (props) => {
  //console.log("props: ", props, props.title)
  return (
    <header>
      <h1>
        <a href='/' onClick={(event) => {event.preventDefault();props.onChangeMode()}}>{props.title}</a>
      </h1>
    </header>
  )
}
//헤더 종료============================================================================

//네비================================================================================
const Nav = (props) => {
  const arrays = []

  for (let index = 0; index < props.topics.length; index++) {
    let element = props.topics[index];
    arrays.push(
      <li key={element.id}>
        <a  href={'/read/' + element.id}
            id={element.id}
            onClick={(event) => { event.preventDefault();
                                  props.onChangeMode(Number(event.target.id));
                                }
                    }>{element.title}
        </a>
      </li>
    )
  }
  return (
    <nav>
      <ol>
        {arrays}
      </ol>
    </nav>
  )
}
//네비 종료============================================================================

//아티클================================================================================
function Article(props) {
  return (
    <article>
      <h2>{props.title}</h2>
      {props.body}
    </article>
  )
}
//아티클 종료============================================================================

//크리에이트================================================================================
function Create(props) {
  return (
    <article>
      <h2>Create</h2>
      <form onSubmit={(event) => {
        event.preventDefault();
        const title = event.target.title.value;
        const body = event.target.body.value;
        props.onCreate(title, body);
      }}>
       
        <p><input type='text' name='title' placeholder='title'></input></p>
        <p><textarea name='body' placeholder='body'></textarea></p>
        <p><input type='submit' value={"만들기"}></input></p>
      </form>
    </article>
  )
}
//크리에이트 종료============================================================================

//업데이트============================================================================
function Update(props){
  const [title, setTitle] = useState(props.title);    //바꿀 수 없는 데이터인 props를 state로 변경
  const [body, setBody] = useState(props.body);
  return (
    <article>
      <h2>Update</h2>
      <form onSubmit={(event) => {
        event.preventDefault();
        // 저장된 데이터가 onUpdate 함수를 통해 App에 전달
        const title = event.target.title.value;
        const body = event.target.body.value;
        props.onUpdate(title, body);
      }}>
       
        {/* onChange를 통해 값을 입력할 때마다 setTitle을 통해 입력한 값을 state에 저장 */}
        <p><input type='text' name='title' placeholder='title' value={title} onChange={(event) => {setTitle(event.target.value); }}></input></p>  
        <p><textarea name='body' placeholder='body' value={body} onChange={(event) => {setBody(event.target.value); }}></textarea></p>
        <p><input type='submit' value={"업데이트"}></input></p>
      </form>
    </article>
)}
//업데이트 종료============================================================================

export default App;

1. 태그의 대소문자
- 소문자는 HTML 태그
- 대문자는 사용자 정의 태그

일반형
function Header() {
  return (
    <header>
      <a href='/'>web</a>
    </header>
  )
}
람다형
const Header = (props) => {
  return (
    <header>
      <a href='/'>web</a>
    </header>
  )
}

============================================================================================

2. 사용자 정의 태그 위치
- App보다 나중에 선언되어도 사용 가능
- React 컴포넌트도 JavaScript 함수나 클래스 선언과 동일한 방식으로 동작하기 때문
--> 컴포넌트는 함수로 정의되기 때문에, 호이스팅의 영향을 받아 컴포넌트가 사용되기 전에 선언될 필요가 없음

============================================================================================

3. prop(속성) - props(속성들)
- html태그의 속성과 같은 역할
- 속성을 부여하여 사용자 정의 태그를 건드리지 않고 내용을 변경시킬 수 있음
- js로 사용할 때는 그냥 사용
- html로 사용할 때는 {} 사용


const Header = (props) => {
  console.log("props: ", props, props.title)
  return (
    <header>
      <a href='/'>{props.title}</a>
    </header>
  )
}
function App(){
    <div>
        <Header title="React"></Header>
    </div>
}

============================================================================================

3.1 속성 배열 사용
function App() {
  const topics = [
    {id:1, title:'html', body:'html is...'}, 
    {id:2, title:'css', body:'css is...'}, 
    {id:3, title:'js', body:'js is...'}, 
  ]
  return (
    <div>
      <Nav topics = {topics}></Nav>
    </div>
  );
}
const Nav = (props) => {
  const arrays = []
  
  for (let index = 0; index < props.topics.length; index++) {
    let element = props.topics[index];
    arrays.push(<li key={element.id}><a href={'/read/' + element.id}>{element.title}</a></li>)
  }

  return(
    <nav>
    <ol>
      {arrays}
    </ol>
  </nav>
  )
}
            1. App()에서 배열 생성
            2. prop으로 배열 전달
            3. Nav에서 props.topics라는 이름의 배열을 전달 받음
            4. 받은 배열을 새로운 배열에 초기화하기 위해 새로운 배열 생성(arrays)
            5. for 문을 사용해서 차례로 element에 전달 받은 배열값 초기화
            --> 차례로 arrays 배열에 주입(push 사용)
                - 이때 생성하는 arrays 배열 안의 속성값으로 props.topics의 값을 사용할 때는 {} 사용

============================================================================================

4. 초기화하지 않은 함수의 일반형과 람다형
function(){
    alert("Header")
}


() => {
    alert("Header")
}

============================================================================================

5. prop으로 이벤트 전달
<Nav    topics={topics} 
        onChangeMode={(id) => {alert(id)}}>
</Nav>
- onChangeMode라는 이름의 속성으로 이하의 이벤트를 사용자 정의 태그에 전달
    function(id){
        alert(id)
    }

const Nav = (props) => {
  const arrays = []

  for (let index = 0; index < props.topics.length; index++) {
    let element = props.topics[index];
    
    arrays.push(
      <li key={element.id}>
        <a  href={'/read/' + element.id} 
            id={element.id}
            onClick={(event) => { event.preventDefault(); 
                                  props.onChangeMode(event.target.id);
                                }
                    }>{element.title}
        </a>
      </li>
    )
  }
  
  return (
    <nav>
      <ol>
        {arrays}
      </ol>
    </nav>
  )
}

    1. onClick이라는 이벤트 속성 추가
    2. 클릭 시 발생할 수 있는 하이퍼링크 이벤트 차단( event.preventDefault(); )
    3. 클릭 시 onChangeMode 속성을 App에서 가져와서 발동
    4. function에서 사용하기 위한 변수를 현재 발동한 태그의 id로 지정(event.target은 이벤트를 유발한 태그 지정)
    5. id값을 가져오기 위해 a태그에 id 속성 부여

============================================================================================

6. state
- prop은 컴포넌트 외부 사용자를 위한 데이터
- state는 컴포넌트 내부 사용자를 위한 데이터

function App() {

  const mode = "READ"
  let content = null;

  if(mode === "WELCOME"){
    content = <Article title="Welcome" body="Hello, web"></Article>
  }
  if(mode === "READ"){
    content = <Article title="Read" body="This is for state"></Article>
  }

  return (
    <div>
      {content}
    </div>
  );
}

    1. mode와 content를 초기화
    2. mode의 값에 따라 content가 변화하는 코드 구현
    3. {content}를 통해 mode의 값에 따라 출력이 변화하는 코드 구현

============================================================================================

7. useState
state를 변경할 때마다 자동으로 화면이 재랜더링되도록 해줌

import { useState } from 'react';
function App() {

  const [mode, setMode] = useState("WELCOME"); // WELCOME이 state의 초기값
                                               // mode[0] 현재 state의 값
                                               // mode[1] 현재 state의 값을 변경할 때 사용하는 함수
  let content = null;

  if(mode === "WELCOME"){
    content = <Article title="Welcome" body="Hello, web"></Article>
  };
  if(mode === "READ"){
    content = <Article title="Read" body="This is for state"></Article>
  };

  return (
    <div>
      <Header title="Web" onChangeMode={() => {setMode("WELCOME")}}></Header>
      <Nav topics={topics} onChangeMode={(id) => {setMode("READ")}} ></Nav>
      {content}
    </div>
  );
}
    1. useState import
    2. 상수 [state명, setState명] = useState("초기값") 지정
    3. 동적으로 값을 변경할 때, setMode을 활용

============================================================================================

8. useState2
function App() {

  const topics = [
    { id: 1, title: 'html', body: 'html is...' },
    { id: 2, title: 'css', body: 'css is...' },
    { id: 3, title: 'js', body: 'js is...' },
  ];

  const [mode, setMode] = useState("WELCOME");
  const [id, setId] = useState(null);
  let content = null;

  if(mode === "READ"){
    let title, body = null;
    for (let index = 0; index < topics.length; index++) {
      let element = topics[index];
      if(element.id == id){       
        title = topics[index].title;
        body = topics[index].body;
      }
    }
    content = <Article title={title} body={body}></Article>
  };

  return (
    <div>
      <Nav topics={topics} onChangeMode={(id) => {setMode("READ"); setId(id)}} ></Nav>
      {content}
    </div>
  );
}

    1. const [id, setId] = useState(null);
    2. mode가 "READ"일 때, title과 body를 null로 만들고 원하는 데이터를 다시 집어넣기
        - for문을 사용해서 topics의 전체 번호를 조회하여 현재 누른 번호와 일치하는 json 데이터 찾기
        - 현재 누른 번호와 일치하는 json 데이터의 title과 body를 넣기 
    ## if(element.id == id)에서 ===이 아닌 이유
        - 태그의 속성으로 값을 넘기면 int도 String으로 변경됨
        - props.onChangeMode(event.target.id); 에서 값을 가져오는데 id={element.id}를 하면서 id가 int에서 String으로 변경되어 있음
        - ===을 하면 int와 String을 비교하는 것이 되기 때문에 값을 찾아오지 못함
        --> props.onChangeMode(Number(event.target.id));로 바꾸면 === 사용 가능

const Nav = (props) => {
  const arrays = []

  for (let index = 0; index < props.topics.length; index++) {
    let element = props.topics[index];
    arrays.push(
      <li key={element.id}>
        <a  href={'/read/' + element.id} 
            id={element.id}
            onClick={(event) => { event.preventDefault(); 
                                  props.onChangeMode(event.target.id);
                                }
                    }>{element.title}
        </a>
      </li>
    )
  }
  return (
    <nav>
      <ol>
        {arrays}
      </ol>
    </nav>
  )
}

============================================================================================

9. Create
function App() {

  // 변수
  let content = null;

  // useState
  const [mode, setMode] = useState("WELCOME");
  const [id, setId] = useState(null);
  const [topics, setTopics] = useState([  { id: 1, title: 'html', body: 'html is...' },
                                          { id: 2, title: 'css', body: 'css is...' },
                                          { id: 3, title: 'js', body: 'js is...' },
                                      ]);
  const [nextId, setNextId] = useState(4);

  // 조건에 따라 state값 변경
  if(mode === "WELCOME"){
    content = <Article title="Welcome" body="Hello, web"></Article>
  };
  if(mode === "READ"){
    let title, body = null;
    for (let index = 0; index < topics.length; index++) {
      let element = topics[index];
      if(element.id === id){ 
        title = element.title;
        body = element.body;
      }
    }
    content = <Article title={title} body={body}></Article>
  };
  if(mode === "Create"){
    content = <Create onCreate={(newTitle, newBody) => {
                                                          const newTopic = {id: nextId, title: newTitle, body: newBody}
                                                          const newTopics = [...topics];
                                                          newTopics.push(newTopic);
                                                          setTopics(newTopics);
                                                          setMode("READ");
                                                          setId(nextId);
                                                          setNextId(nextId + 1);
                                                        }
                                }
              ></Create>
  }
  // 출력
  return (
    <div>
      <Header title="Web" onChangeMode={() => {setMode("WELCOME")}}></Header>
      
      <Nav topics={topics} onChangeMode={(id) => {setMode("READ"); setId(id)}} ></Nav>
      
      {content}

      <a href='/create' onClick={(event) => {event.preventDefault(); setMode("Create");}}>Create</a>
    </div>
  );
}

function Create(props) {
  return (
    <article>
      <h2>Create</h2>
      <form onSubmit={(event) => {
        event.preventDefault(); 
        const title = event.target.title.value;
        const body = event.target.body.value;
        props.onCreate(title, body);
      }}>
        
        <p><input type='text' name='title' placeholder='title'></input></p>
        <p><textarea name='body' placeholder='body'></textarea></p>
        <p><input type='submit' value={"만들기"}></input></p>
      </form>
    </article>
  )
}

    1. App - 하이퍼링크 추가
    <a href='/create' onClick={(event) => {event.preventDefault(); setMode("Create");}}>Create</a>
    2. App - useState 추가
    const [topics, setTopics] = useState([  { id: 1, title: 'html', body: 'html is...' },
                                          { id: 2, title: 'css', body: 'css is...' },
                                          { id: 3, title: 'js', body: 'js is...' },
                                      ]);
    const [nextId, setNextId] = useState(4);
    - topics: 이 배열에 새롭게 추가될 것
    - nextId: topics 배열의 id 부여에 추가될 것
    3. Create - form 작성
    4. App - onCreate 함수 구현(newTopic 작성까지)
    - Create에서 title과 body를 넘기면 해당 데이터를 바탕으로 const newTopic를 만듦
    5. Create - onSubmit 함수 구현
    - 기본 이벤트(페이지 새로고침)을 방지하고, event.target을 사용하여 title과 body 데이터를 불러옴
    - App에서 구현된 onCreate 함수를 props.onCreate를 통해 title과 body를 App으로 전달
    6. App - 받아온 데이터를 배열에 넣음
    - const newTopics = [...topics];    기존 배열을 복사
    - newTopics.push(newTopic);         복사한 배열에 받아온 데이터를 추가
    - setTopics(newTopics);             데이터를 추가한 새로운 배열을 통채로 set
    - setMode("READ");                  모드 변경
    - setId(nextId);                    id 값을 현재 아이디 값으로 변경
                                        --> 새로 입력한 데이터가 출력됨
    - setNextId(nextId + 1);            다음 데이터를 입력할 때는 초기값인 4가 아닌 5가 입력

============================================================================================

10. Update
function App() {

  if(mode === "READ"){
    let title, body = null;
    for (let index = 0; index < topics.length; index++) {
      let element = topics[index];
      if(element.id === id){ 
        title = element.title;
        body = element.body;
      }
    }
    content = <Article title={title} body={body}></Article>
    contextControl = <li><a href={'/update/'+id} onClick={(event) => {event.preventDefault(); setMode("Update");}}>Update</a></li>
  };
  if(mode === "Update"){
    let title, body = null;
    for (let index = 0; index < topics.length; index++) {
      let element = topics[index];
      if(element.id === id){ 
        title = element.title;
        body = element.body;
      }
    }
    content = <Update title={title} body={body} onUpdate={(updatedTitle, updatedBody) => {
                                                          const updatedTopic = {id: id, title: updatedTitle, body: updatedBody}
                                                          const updatedTopics = [...topics];
                                                          for (let index = 0; index < topics.length; index++) {
                                                            const element = topics[index];
                                                            if(element.id === id){
                                                              updatedTopics[index] = updatedTopic;   // 기존 데이터를 갱신
                                                              break;
                                                            }
                                                          }
                                                          setTopics(updatedTopics);
                                                          setMode("READ");
                                                        }
                                }
              ></Update>
  }

  // 출력
  return (
    <div>
      <ul>
        <li><a href='/create' onClick={(event) => {event.preventDefault(); setMode("Create");}}>Create</a></li>
        {contextControl}
      </ul>
    </div>
  );
}

function Update(props){
  const [title, setTitle] = useState(props.title);    //바꿀 수 없는 데이터인 props를 state로 변경
  const [body, setBody] = useState(props.body);
  return (
    <article>
      <h2>Update</h2>
      <form onSubmit={(event) => {
        event.preventDefault(); 
        // 저장된 데이터가 onUpdate 함수를 통해 App에 전달
        const title = event.target.title.value;
        const body = event.target.body.value;
        props.onUpdate(title, body);
      }}>
        
        {/* onChange를 통해 값을 입력할 때마다 setTitle을 통해 입력한 값을 state에 저장 */}
        <p><input type='text' name='title' placeholder='title' value={title} onChange={(event) => {setTitle(event.target.value); }}></input></p>  
        <p><textarea name='body' placeholder='body' value={body} onChange={(event) => {setBody(event.target.value); }}></textarea></p>
        <p><input type='submit' value={"업데이트"}></input></p>
      </form>
    </article>
)}

    1. App - if(mode === "READ")일 때 업데이트가 출력되도록 코드 추가
    contextControl = <li><a href={'/update/'+id} onClick={(event) => {event.preventDefault(); setMode("Update");}}>Update</a></li>
    2. App - 출력하는 부분에 contextControl 추가
    {contextControl}
    3. App - if(mode === "Update")일 때 현재 출력되는 값을 찾기(READ와 동일)
        let title, body = null;
        for (let index = 0; index < topics.length; index++) {
        let element = topics[index];
        if(element.id === id){ 
            title = element.title;
            body = element.body;
            }
        }
    ==> 이를 통해 현재 업데이트할 데이터의 title과 body 정보를 가져올 수 있음
    4. App - Update 함수로 title와 body 데이터 넘겨주기
        content = <Update title={title} body={body}></Update>

    5. Update - Update 함수 구현
        A. props 데이터를 state 데이터로 바꾸기
            - props 데이터는 외부에서 받는 것이기 떄문에 변경되지 않아 내부 사용자를 위한 데이터인 state로 변경
            const [title, setTitle] = useState(props.title); 
            const [body, setBody] = useState(props.body);
        B. value에 받아온 title과 body를 속성으로 추가
            value={title}
            value={body}
        C. onChange를 통해 값을 입력할 때마다 setTitle / setBody를 사용하여 state에 입력한 값을 저장
            onChange={(event) => {setTitle(event.target.value);
            onChange={(event) => {setBody(event.target.value);
        =>  props.onUpdate(event.target.title.value;, event.target.body.value;);  
            를 통해 다시 App으로 변경된 데이터가 전송
    
    6. App - Update 함수에서 받은 데이터를 바탕으로 기존 데이터 재구성하기

        
        A. const updatedTopic = {id: id, title: updatedTitle, body: updatedBody}
            - 받아온 데이터로 updatedTopic 구성
            - id는 Read 상태일 때 출력되기 때문에 그대로 사용 가능
        B. const updatedTopics = [...topics];
            - 기존 배열 복사
        C.  for (let index = 0; index < topics.length; index++) {
              const element = topics[index];
                if(element.id === id){
                    updatedTopics[index] = updatedTopic;
                    break;
                }
            }
            - for문을 사용하여 배열 속 id 중 현재 id와 동일한 값을 찾고, 해당 데이터를 갱신
        D.  setTopics(updatedTopics);
            - 데이터를 갱신한 새 배열을 set
        E. setMode("READ");
            - Read 상태로 변경
            - id가 그대로 유지되기 때문에 id를 set할 필요는 없음

============================================================================================

11. Delete
contextControl = <>
      <li><a href={'/update/'+id} onClick={(event) => {event.preventDefault(); setMode("Update");}}>Update</a></li>
      <li><input type='button' value="삭제" onClick={()=>{
                        const newTopics = [];
                        for (let index = 0; index < topics.length; index++) {
                            const element = topics[index];
                            if(element.id !== id){
                                newTopics.push(element);
                            }
                        }
                        setTopics(newTopics);
                        setMode("WELCOME")
                        }}>
      </input></li>
</>

    1. <></> 빈 태그를 생성하고 update 옆에 delete 버튼 배치
    2. onClick 이벤트 구현
        A. const newTopics = [];
        - 빈 배열 생성
        B. for (let index = 0; index < topics.length; index++) {
                const element = topics[index];
                if(element.id !== id){
                    newTopics.push(element);
                }
            }
        - for문을 사용하여 현재 아이디와 일치하지 않는 데이터만 배열에 추가
        C. setTopics(newTopics);
        - 새 배열을 set
        D. 현재 id가 없기 때문에 기본값인 WELCOME으로 모드 변경

============================================================================================


'단순 코드 기록 > RE: React' 카테고리의 다른 글

redux toolkit 기초  (0) 2024.05.23
redux 기초  (0) 2024.05.22
Context Api 기초  (0) 2024.05.22
Styled Components 기초  (0) 2024.05.22
Router 기초  (0) 2024.05.21