React - Create

정원·2023년 1월 26일
0

React

목록 보기
7/42

2023.01.26 Create

Create 클릭 시
제목과 본문내용을 입력하고
li로 추가되는 어플리케이션을 만들어보자.

Create 페이지로 이동하는 a 태그 선언.

<a href='/create'>Create</a>

Create 클릭 시 mode가 'CREATE'로 바뀌고
새로운 ui가 나타나도록 할것이다.

a태그에 이벤트 추가.
a태그 기본기능 막고 클릭 시 mode가 'CREATE'로 변경.

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

mode가 'CREATE'일 때
if문에서 content내용을 변경해주면 된다.

 else if(mode === 'CREATE') {
    content = <Create></Create>
  }

CREATE는 li도 만들어야하기 때문에
따로 컴포넌트를 만들어서 사용하자.

CREATE 컴포넌트 만들기

입력값을 전달하기 위해 form태그 사용.
input,textarea 사용하고 이 태그들은 인라인 소요이기 때문에
가로정렬이 된다.
p 태그로 감싸서 세로정렬을 진행.

function Create() {
  return <article>
    <h2>Create</h2> 
    <form>
      <p><input type="text" name="title" placeholder="title" /></p>
      <p><textarea name='body' placeholder='body'></textarea></p>
      <p><input type="submit" value="Create"></input></p>
    </form>
  </article>
}

이렇게 하면 create 버튼 클릭시 제목과 본문을 입력할 수 있는 ui가 나타난다.


Create 컴포넌트를 이용하는 이용자가 생성버튼을 눌렀을때
후속작업을 할 수있게 인터페이스를 제공해보자.

onCreate( )함수 추가하고 onCreate는 title,body를 받는다.

else if(mode === 'CREATE') {
    content = <Create onCreate={(title, body)=>{
      
    }}></Create>
  }

onCreate함수를 호출할 수 있도록 Create 컴포넌트 수정.

form태그에 onSubmit( ) prop를 제공한다.
onSubmit( ) 은 submit 버튼을 눌렀을 때 form 태그에서 발생하는 이벤트이다.

form태그는 submit을 하면 페이지가 리로드 되기 때문에 기능을 막아야한다.
event.preventDefault(); 사용.

그 후 form 태그의 title,body의 value를 얻어와야한다.

<form onSubmit={event=>{
      event.preventDefault();
      const title = event.target.title.value;
      const body = event.target.body.value;
      
    }}>

이제 title, body의 value를 Create 컴포넌트 사용자에게 공급해야한다.
사용자는 onCreate를 이용해서 submit정보를 공급받는다.
그렇기 때문에 Create 컴포넌트에 props를 주고 onCreate( )를 호출하고
매개변수로 title과 body를 준다.

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" /></p>
      <p><textarea name='body' placeholder='body'></textarea></p>
      <p><input type="submit" value="Create"></input></p>
    </form>
  </article>
}

이제 topics변수에 새로운 원소를 추가해서 li목록이 추가되도록 해보자.
topics를 state로 승격시키기.

const [topics, setTopics] = useState([
    {id:1, title: 'html', body:'html is...'},
    {id:2, title: 'css', body:'css is...'},
    {id:3, title: 'jsvascript', body:'jsvascript is...'},
  ]);

topics의 새로운 원소 만들기.
newTopic 객체 생성하고 객체의 id가 필요하다.

else if(mode === 'CREATE') {
    content = <Create onCreate={(_title, _body)=>{
      const newTopic = {title: _title, body:_body}
    }}></Create>
  }

id값을 별도로 관리하기 위해 nextId state 생성하고 newTopic 객체에 id를 추가한다.

const[nextId, setNextId] = useState(4);
 else if(mode === 'CREATE') {
    content = <Create onCreate={(_title, _body)=>{
      const newTopic = {id:nextId, title: _title, body:_body}
    }}></Create>
  }

newTopic 생성이 완료 되었으니 이제 topics에 추가를 해야한다.

topics.push(newTopic);

topics는 값을 읽을때 사용하는 것이기 때문에 추가할 수 없다.

그렇다면 setTopics를 쓰면 될까?

setTopics(topics);

이렇게 해도 아무일도 일어나지 않는다..!!!


state를 사용할때

  • PRIMITIVE(원시) 타입이면 지금까지 했던 방식을 사용하면 된다.
    (String,Number,Boolean,null)

  • 하지만 Object 타입이라면 newValue = {...value} 를 사용해야한다.
    (Object,Array)

  • newValue = {...value}
    ...value : 데이터를 복제해서 newValue에 넣는다.

  • newValue를 변경한다.(복제본)

  • setValue(newValue)
    newValue를 넣으면 컴포넌트가 다시 실행된다.


위 내용을 참고하여 코드를 다시 수정해보자.

const newTopics = [...topics] //복제본
newTopics.push(newTopic); //복제본에 푸쉬
setTopics(newTopics); //topics에 추가

리액트는 오리지널 topics와 새로들어온 newTopics를 비교해서
내용이 다르면 컴포넌트를 다시 렌더링해준다.

이렇게 하면 완성!


작성 후 상세보기로 바로 이동

create 클릭 - 글작성 - 글상세보기로 바로이동하기

content = <Create onCreate={(_title, _body)=>{
      const newTopic = {id:nextId, title: _title, body:_body}
      const newTopics = [...topics] //복제본
      newTopics.push(newTopic); //복제본에 푸쉬
      setTopics(newTopics); //topics에 추가
      //새 li추가 후 mode를 READ로 변경하여 상세보기로 이동
      setMode('READ');
      //현재글을 nextId로 지정
      setId(nextId);
      //다음 글추가할때를 위해 현재 id+1
      setNextId(nextId+1);
    }}></Create>

✨ state 사용할때 타입에 따른 사용법 개념을 다시 정리해보자.
  • 타입이 Object일때,
const [value, setValue] = useState([1]);
배열
value.push(2);
오리지널 데이터 변경
setValue(value);
오리지널 데이터 입력

리액트는 setValue를 호출했을때 오리지널 데이터와 새로들어온 데이터가
같으면 컴포넌트를 다시 렌더링하지 않는다.

  • 타입이 원시타입일때,
const [value, setValue] = useState(1);
원시타입
setValue(2);
새로운 데이터 입력

오리지널 데이터와 새로 들어온 데이터가 다르기 때문에 컴포넌트를 다시 렌더링한다.

  • 이렇기 때문에 타입이 object일때는
const [value, setValue] = useState([1]);
배열
newValue = [...value]
오리지널 데이터 복제
newValue.push(2);
복제한 데이터 변경
setValue(newValue);
변경한 데이터를 set

✨ 전체코드

import logo from './logo.svg';
import './App.css';
import { useState } from 'react';

function Header(props) {
  return <header>
      <h1><a href='/' onClick={(event)=>{
        event.preventDefault();
        props.onChangeMode();
      }}>{[props.title]}</a></h1>
    </header>
}

function Nav(props){
   const lis = [   ]
   for(let i=0; i<props.topics.length; i++) {
    let t = props.topics[i];
    lis.push(<li key={t.id}>
      <a id={t.id} href={'/read/'+t.id} onClick={(event)=>{
        event.preventDefault();
        props.onChangeMode(Number(event.target.id));
      }}>{t.title}</a>
      </li>)
   }
  return <nav>
      <ol>
        {lis}
      </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" /></p>
      <p><textarea name='body' placeholder='body'></textarea></p>
      <p><input type="submit" value="Create"></input></p>
    </form>
  </article>
}

function App() {
  // const _mode = useState('WELCOME');
  // const mode = _mode[0];
  // const setMode = _mode[1];

  const [mode, setMode] = useState('WELCOME');
  
  // console.log('_mode', _mode);
  
  //li클릭시에 state로 변경하기 위해 id,setId 변수선언, 현재 값이 선택되지 않았기 때문에 null
  const [id, setId] = useState(null);

  const[nextId, setNextId] = useState(4);

  const [topics, setTopics] = useState([
    {id:1, title: 'html', body:'html is...'},
    {id:2, title: 'css', body:'css is...'},
    {id:3, title: 'jsvascript', body:'jsvascript is...'},
  ]);

  let content = null;

  if(mode === 'WELCOME'){
    content = <Article title="Welcome" body="Hello, WEB"></Article>
    const value = [1];
    console.log('value :' + value);

    value.push(2);
    console.log('value : ' + value);
  } else if(mode === 'READ') {
    let title, body = null; //title,body 초기화
    //반복문 이용해서 선택한 id(state)와 일치하는 li의 id를 찾아서 그 값을 얻어옴
    for(let i=0; i<topics.length; i++) {
      console.log(topics[i].id, id);
      if(topics[i].id === id) {
        title = topics[i].title;
        body = topics[i].body;
      }
    }
    content = <Article title={title} body={body}></Article>
  }  else if(mode === 'CREATE') {
    content = <Create onCreate={(_title, _body)=>{
      const newTopic = {id:nextId, title: _title, body:_body}
      const newTopics = [...topics] //복제본
      newTopics.push(newTopic); //복제본에 푸쉬
      setTopics(newTopics); //topics에 추가
      //새 li추가 후 mode를 READ로 변경하여 상세보기로 이동
      setMode('READ');
      //현재글을 nextId로 지정
      setId(nextId);
      //다음 글추가할때를 위해 현재 id+1
      setNextId(nextId+1);
    }}></Create>
  }
  return (
      <div>
        <Header title="REACT" onChangeMode={()=>{
          // alert('Header');
          // mode = 'WELCOME' 값을 바꿀때는 setMode를 사용한다.
          setMode('WELCOME');
        }}></Header>
        <Nav topics={topics} onChangeMode={(_id)=>{
          // alert(id);
          // mode = 'READ' 
          setMode('READ');
          //onChangeMode 매개변수로 _id를 받고 setId를 통해 id를 변경
          setId(_id);
        }}></Nav>
        {content}
        <a href='/create' onClick={event=>{
          event.preventDefault();
          setMode('CREATE');
        }}>Create</a>
      </div>
  );
}

export default App;

Component가 화면에 그려지는 과정




업로드중..

0개의 댓글