다른 온라인 강의/생활코딩- React 2022년 개정판

3) state, create

backend dev 2024. 6. 18.

 

State

컴포넌트에서 Prop은 전달인자 return은 반환값이다.

동적으로 컴포넌트의 내용을 바꾸기 위해서는 State를 이용한다.

state의 값이 바뀌면 컴포넌트가 다시 실행되어서 return의 구성이 변경될수있고, 

새롭게 렌더링 된다.

 

+ gpt

Prop과 State에 대한 설명

  1. Prop (Properties)
    • 전달인자: Prop은 컴포넌트가 외부로부터 전달받는 데이터입니다. 부모 컴포넌트가 자식 컴포넌트에 데이터를 전달할 때 사용됩니다.
    • 불변성: Prop은 컴포넌트 내에서 변경할 수 없습니다. 자식 컴포넌트는 부모 컴포넌트로부터 전달받은 Prop을 읽기 전용으로 사용합니다.
    • 사용 예: <Article title="Welcome" body="Hello, Web" />에서 title과 body가 Prop입니다.
  2. State
    • 내부 데이터: State는 컴포넌트가 관리하는 내부 데이터입니다. State는 컴포넌트 내에서 변경될 수 있습니다.
    • 변경 가능성: State의 값이 변경되면 해당 컴포넌트가 다시 렌더링됩니다. 이는 컴포넌트의 UI를 동적으로 변경할 수 있게 합니다.
    • 사용 예: const [mode, setMode] = useState('WELCOME');에서 mode가 State입니다.

렌더링과 재렌더링

  • 동적 변경: State를 사용하면 컴포넌트의 내용을 동적으로 변경할 수 있습니다. State의 값이 변경되면 React는 해당 컴포넌트를 다시 렌더링합니다.
  • 렌더링 과정: 컴포넌트의 State가 변경되면, React는 해당 컴포넌트를 다시 실행하고, 새로운 return 값을 바탕으로 UI를 업데이트합니다.
import './App.css';
import Header from './Header';
// 사용자 정의 태그를 만들때는 대문자로 시작해야한다.

import {useState} from 'react'; // state를 사용하기위해서 react에서 제공하는 useState라는 훅을 사용해야한다.

function Nav(props) {

// JSX 문법을 이용하면 배열에 태그를 저장할수도있다.
// 리스트의 각각의 태그는 key라는 프롭(속성)을 가져야하고 그 값을 유니크해야한다.
    const lis = props.topics.map((p) => (
        <li key={p.id}>
            <a id={p.id} href={'/read/' + p.id} onClick={(event) => {
                event.preventDefault();
                props.onChangeMode(parseInt(event.target.id)); //event.target은 이벤트가 발생한 태그를 의미 , 속성의 값은 문자열이므로 숫자 타입으로 변경해준다.
            }}> {p.title}</a>
        </li>
    ))

    return (
        <nav>
            <ol>
                {lis}
            </ol>
        </nav>
    );
}

function Article(props) {
    return (
        <article>
            <h2> {props.title} </h2>
            {props.body}
        </article>
    );
}

function App() {
    // App이라는 컴포넌트는 한번 실행되면 다시 실행되지않는다. 그래서 이벤트함수에서 값을 변경해도 App컴포넌트가 다시실행되지않으므로
    // return되는 요소의 값들이 바뀌지않는다.
    // 다시 컴포넌트를 실행하기위해 state를 사용한다. state를 이용하여 App 컴포넌트가 다시 실행되게끔 한다.
    //userState는 배열을 반환하고 첫번째 요소는 상태의값 데이터, 두번째요소는 상태의 값을 변경할때 사용하는 함수이다.
    // const _mode = useState('WELCOME'); // 상태 초기값은 설정해줘야한다.
    // const mode =_mode[0]; // 첫번째 요소는 상태의값 데이터
    // const setMode =_mode[1]; // 두번째요소는 상태의 값을 변경할때 사용하는 함수
    //위의 코드는 너무 길어지므로 아래와 같이 간단하게 사용가능
    const [mode, setMode] = useState('WELCOME');

    const [id, setId] = useState(null); // 상태의 기본값으로 null도 가능

    const topics = [
        {id: 1, title: 'html', body: 'html is ...'},
        {id: 2, title: 'css', body: 'cs is ...'},
        {id: 3, title: 'javascript', body: 'javascript is ...'}
    ]
    let content = null;
    if (mode === 'WELCOME') {
        // id state를 이용하여 내용을 수정해준다.
        content = <Article title="WELCOME WEB" body="HELLO , WEB"></Article>
    } else if (mode === 'READ') { // find로 id 상태값과 같은 토픽을 찾는다. -> find메소드는 반복문과 거의비슷하므로 반복문으로 구성해도된다.
        let topic = topics.find(p => p.id === id); // 이렇게 찾아쓰는게 토픽에 해당 인덱스가 없는것에서 좀더 안전하다.
        if(topic){
            content = <Article title={topic.title} body={topic.body}></Article>
        }
        else{
            content = <Article title="not found" body="not found" ></Article>
        }

    }
    return (
        <div className="App">
            <Header title="REACT" onChangeMode={() => {
                setMode('WELCOME') //상태의 값을 변경할때 사용하는 함수를 사용해준다.
            }}></Header>
            <Nav topics={topics} onChangeMode={(id) => {
                setMode('READ') //상태의 값을 변경할때 사용하는 함수를 사용해준다.
                setId(id) // 전달인자인 클릭된 a태그의 아이디값을 이용하여 id state를 변경해준다.
            }}></Nav>
            {content}
        </div>
    );
}

export default App;

 

 


Create

state로 관리할 값이 primitive 타입이라면 그냥 사용하면 되지만

 

객체 타입이라면  상태값을 변경할때 주의해야한다.

 

기존 상태값을 복사해서 변경한뒤 그 복제된 값을 넘겨줘야한다.

 

그이유 -> gpt

React 컴포넌트에서 상태를 관리할 때, 상태가 변경되면 React는 해당 컴포넌트를 다시 렌더링합니다. 이때 React는 상태의 변화를 감지하기 위해 현재 상태와 변경된 상태를 비교합니다. 객체나 배열과 같은 복합 데이터 타입은 참조 타입이기 때문에, 해당 객체나 배열이 변경되었는지를 직접적으로 비교할 수 없습니다. 따라서 객체의 경우, 객체를 직접 수정하면 React는 상태 변화를 감지하지 못할 수 있습니다

그래서

React에서는 객체나 배열을 변경할 때 기존 상태를 직접 수정하지 않고, 기존 상태를 복제하여 변경된 값을 새로운 상태로 설정해야 합니다. 이렇게 하면 React가 상태 변화를 감지할 수 있고, 필요할 때만 다시 렌더링할 수 있습니다.

 

 

복제는 스프레드 연산자인 ...을 이용하면 된다.

 

create 버튼추가

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

mode 상태값을 CREATE로 변경시켜주는 기능을 가지고있다.

 

else if (mode === 'CREATE') {
    content = <Create onCreate={(title,body)=> { // Create 컴포넌트의 프롭(props)으로 함수를 전달한다.
        const newTopic = {id:nextId ,title : title,body : body};
        const newTopics = [...topics]; // 원래 topics를 복사해서
        newTopics.push(newTopic); // 새로 추가한 토픽을 추가하고
        setTopics(newTopics); // topics 상태값을 변경해줘야 리액트가 상태변화감지를 하여 컴포넌트를 재실행한다.[다시 렌더링]

        // 글을 추가후 해당 글로 이동
        setMode('READ');
        setId(newTopic.id);

        setNextId(nextId+1);
    }}></Create>
}

mode 상태값을 CREATE로 변경되면 App의 컴포넌트가 재실행되면서 다음의 코드가 동작된다.

 

Create라는 컴포넌트에 onCreate라는 Props을 설정하고 onCreate는 title , body값을 전달받으면

topics라는 상태값에 추가할 topic을 만들고 topics라는 상태값을 수정하는 함수이다.

 

그리고 Mode,Id,NextId라는 상태값도 수정하는 기능도 존재한다.

 

관리되는 상태가 객체라면 스프레드함수인 ...와 같은 객체복사방법을 통해 객체를 복사해서 상태값을 변경해야
리액트가 상태값이 변경됬음을 인지하고 컴포넌트를 재실행[렌더링]한다.

 

function Create(props) {
    return ( // onSubmit 이라는 jsx 문법을 이용하여 submit 버튼이 눌렸을떄 발생할
        /*
        리액트에서는 html의 기본태그 또한 컴포넌트로 취급하며, 속성은 props(프롭)으로 취급한다.
        onSubmit은 리액트에서 제공하고 jsx 문법인 props 이다.
        form 태그에 onSubmit props 을 이용하여 submit버튼이 눌렸을때 발생하는 이벤트를 지정할 수 있다.
        form 태그는 submit이 되었을때 페이지가 리로딩 되므로 해당 태그의 기본 동작을 막고 이벤트 리스너 함수를 지정해준다.
         */
        <article>
            <h2>Create</h2>
            <form onSubmit={(event) =>{
                event.preventDefault();
                // input 요소는 폼의 일부로서, 폼의 submit 이벤트가 발생하면 폼 전체가 이벤트의 타겟이 됩니다
                const title = event.target.title.value;  // form 태그안의 name이 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"/></p>
            </form>
        </article>
    );
}

create 컴포넌트는 article 태그를 반환하며 그 안에 form 태그가 존재한다.

리액트에서 제공하는 onSubmit Props을 이용하여 submit 버튼이 눌렸을때 이벤트를 설정할 수 있다.

 

전체코드

import './App.css';
import Header from './Header';
// 사용자 정의 태그를 만들때는 대문자로 시작해야한다.

import {useState} from 'react'; // state를 사용하기위해서 react에서 제공하는 useState라는 훅을 사용해야한다.

function Nav(props) {

// JSX 문법을 이용하면 배열에 태그를 저장할수도있다.
// 리스트의 각각의 태그는 key라는 프롭(속성)을 가져야하고 그 값을 유니크해야한다.
    const lis = props.topics.map((p) => (
        <li key={p.id}>
            <a id={p.id} href={'/read/' + p.id} onClick={(event) => {
                event.preventDefault();
                props.onChangeMode(parseInt(event.target.id)); //event.target은 이벤트가 발생한 태그를 의미 , 속성의 값은 문자열이므로 숫자 타입으로 변경해준다.
            }}> {p.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 ( // onSubmit 이라는 jsx 문법을 이용하여 submit 버튼이 눌렸을떄 발생할
        /*
        리액트에서는 html의 기본태그 또한 컴포넌트로 취급하며, 속성은 props(프롭)으로 취급한다.
        onSubmit은 리액트에서 제공하고 jsx 문법인 props 이다.
        form 태그에 onSubmit props 을 이용하여 submit버튼이 눌렸을때 발생하는 이벤트를 지정할 수 있다.
        form 태그는 submit이 되었을때 페이지가 리로딩 되므로 해당 태그의 기본 동작을 막고 이벤트 리스너 함수를 지정해준다.
         */
        <article>
            <h2>Create</h2>
            <form onSubmit={(event) =>{
                event.preventDefault();
                // input 요소는 폼의 일부로서, 폼의 submit 이벤트가 발생하면 폼 전체가 이벤트의 타겟이 됩니다
                const title = event.target.title.value;  // form 태그안의 name이 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"/></p>
            </form>
        </article>
    );
}

function App() {
    // App이라는 컴포넌트는 한번 실행되면 다시 실행되지않는다. 그래서 이벤트함수에서 값을 변경해도 App컴포넌트가 다시실행되지않으므로
    // return되는 요소의 값들이 바뀌지않는다.
    // 다시 컴포넌트를 실행하기위해 state를 사용한다. state를 이용하여 App 컴포넌트가 다시 실행되게끔 한다.
    //userState는 배열을 반환하고 첫번째 요소는 상태의값 데이터, 두번째요소는 상태의 값을 변경할때 사용하는 함수이다.
    // const _mode = useState('WELCOME'); // 상태 초기값은 설정해줘야한다.
    // const mode =_mode[0]; // 첫번째 요소는 상태의값 데이터
    // const setMode =_mode[1]; // 두번째요소는 상태의 값을 변경할때 사용하는 함수
    //위의 코드는 너무 길어지므로 아래와 같이 간단하게 사용가능
    const [mode, setMode] = useState('WELCOME');

    const [id, setId] = useState(null); // 상태의 기본값으로 null도 가능
    const [nextId,setNextId] = useState(4);
    const [topics,setTopics] = useState( [
        {id: 1, title: 'html', body: 'html is ...'},
        {id: 2, title: 'css', body: 'cs is ...'},
        {id: 3, title: 'javascript', body: 'javascript is ...'}
    ])
    let content = null;
    if (mode === 'WELCOME') {
        // id state를 이용하여 내용을 수정해준다.
        content = <Article title="WELCOME WEB" body="HELLO , WEB"></Article>
    } else if (mode === 'READ') { // find로 id 상태값과 같은 토픽을 찾는다. -> find메소드는 반복문과 거의비슷하므로 반복문으로 구성해도된다.
        let topic = topics.find(p => p.id === id); // 이렇게 찾아쓰는게 토픽에 해당 인덱스가 없는것에서 좀더 안전하다.
        if(topic){
            content = <Article title={topic.title} body={topic.body}></Article>
        }
        else{
            content = <Article title="not found" body="not found" ></Article>
        }
    }else if (mode === 'CREATE') {
        content = <Create onCreate={(title,body)=> { // Create 컴포넌트의 프롭(props)으로 함수를 전달한다.
            const newTopic = {id:nextId ,title : title,body : body};
            const newTopics = [...topics]; // 원래 topics를 복사해서
            newTopics.push(newTopic); // 새로 추가한 토픽을 추가하고
            setTopics(newTopics); // topics 상태값을 변경해줘야 리액트가 상태변화감지를 하여 컴포넌트를 재실행한다.[다시 렌더링]

            // 글을 추가후 해당 글로 이동
            setMode('READ');
            setId(newTopic.id);

            setNextId(nextId+1);
        }}></Create>
    }
    return (
        <div className="App">
            <Header title="REACT" onChangeMode={() => {
                setMode('WELCOME') //상태의 값을 변경할때 사용하는 함수를 사용해준다.
            }}></Header>
            <Nav topics={topics} onChangeMode={(id) => {
                setMode('READ') //상태의 값을 변경할때 사용하는 함수를 사용해준다.
                setId(id) // 전달인자인 클릭된 a태그의 아이디값을 이용하여 id state를 변경해준다.
            }}></Nav>
            {content}
            <a href="/create" onClick={event =>{
                event.preventDefault();
                setMode('CREATE');
            }}>Create</a>
        </div>
    );
}

export default App;

 

 

댓글