글 작성자: 망고좋아
반응형

📖 오늘 배운 내용 - 2022.01.13

  • react-redux
  • 테스트

 

📝 리덕스 없이 리액트 구현 :: props drilling

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

function App() {
  const [value, setValue]  = useState(0);
  return (
    <div>
      <h1>Root</h1>
      {value}
      <div id="container">
        <Left1 number={value}></Left1>
        <Right1 onIncrease={() => { 
          setValue(value+1)
        }}></Right1>
      </div>
    </div>
  );
}

function Left1(props){
  return <div>
    <h2>Left1 : {props.number}</h2>
    <Left2 number={props.number}></Left2>
  </div>
}
function Left2(props){
  return <div>
    <h3>Left2 : {props.number}</h3>
    <Left3 number={props.number}></Left3>
  </div>
}
function Left3(props){
  return <div>
    <h4>Left3 : {props.number}</h4>
    <Left4 number={props.number}></Left4>
  </div>
}
function Left4(props){
  return <div>
    <h5>Left4 : {props.number}</h5>

  </div>
}


function Right1(props){
  return <div>
    <h2>Right1</h2>
    <Right2 onIncrease={() => {
      props.onIncrease();
    }}></Right2>
  </div>
}
function Right2(props){
  return <div>
    <h3>Right2</h3>
    <Right3 onIncrease={() => {
      props.onIncrease();
    }}></Right3>
  </div>
}
function Right3(props){
  return <div>
    <h4>Right3</h4>
    <Right4 onIncrease={() => {
      props.onIncrease();
    }}></Right4>
  </div>
}
function Right4(props){
  return <div>
    <h5>Right4</h5>
    <input type="button" value="+" onClick={() => { 
      props.onIncrease();
    }}></input>    
  </div>
}

export default App;

 

📝 리덕스로 구현

  • npm install redux react-redux
  • Provider는 Provider 하위에서 컴포넌트를 사용할 거다 라는 말이다.
  • useSelector는 함수를 인자로 받는다.

 

🛠 index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from 'react-router-dom';
import {createStore} from 'redux';
import {Provider, useDispatch, useSelector} from 'react-redux';
function reducer(oldState, action){
  if(oldState === undefined){
      return {
          mode:'WELCOME', 
          topics:[
              {id:1, title:'html', body:'html is ..'},
              {id:2, title:'css', body:'css is ..'}
          ],
          nextId:3
      }
  }
  const newState = {...oldState}
  if(action.type === 'CHANGE_MODE'){
      newState.mode = action.mode;
  } else if(action.type === 'CHANGE_ID'){
      newState.id = action.id;
  } else if(action.type === 'CREATE'){
      const newTopics = [...newState.topics];
      newTopics.push({
          id:newState.nextId,
          title:action.title,
          body:action.body
      });
      newState.topics = newTopics;
  } else if(action.type === 'UPDATE'){
      const newTopics = [...newState.topics];
      for(let i=0; i<newTopics.length; i++){
          let t = newTopics[i];
          if(t.id === action.id){
              t.title = action.title;
              t.body = action.body;
          }
      }
      newState.topics = newTopics;
  } else if(action.type === 'DELETE'){
      let newTopics = [];
      for(let i=0; i<newState.topics.length; i++){
          let t = newState.topics[i];
          if(t.id !== action.id){
              newTopics.push(t);
          }
      }
      newState.topics = newTopics;
  } else if(action.type === 'INCREASE_NEXT_ID'){
      newState.nextId++;
  } 

  return newState;
}
const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());


ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Provider store={store}>
        <App />
      </Provider>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

🛠 App.js

import './App.css';
import {useState} from 'react';
import {Link,Routes,Route,useParams,useNavigate} from 'react-router-dom';
import {Provider, useDispatch, useSelector} from 'react-redux';



function Header(props){
  return <header>
      <h1><Link to="/">{props.title}</Link></h1>
    </header>
}
function Nav(props){
  let lis = []
  for(let i=0; i<props.data.length; i=i+1){
    let d = props.data[i];
    lis.push(<li key={d.id}><Link to={'/read/'+d.id}>{d.title}</Link></li>)
  }
  return <nav>
    <ol>
        {lis}
    </ol>
  </nav>
}
function Article({title, body}){
  console.log('Article');
  return <article>
    <h2>{title}</h2>
    {body}
  </article>
}
function Create(props){
  function submitHandler(evt){
    evt.preventDefault();
    let title = evt.target.title.value;
    let body = evt.target.body.value;
    props.onSubmit(title, body)
    console.log(title);
  }
  return <article>
    <h2>Create</h2>
    <form onSubmit={submitHandler}>
      <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 Update(props){
  const params = useParams();
  const id = Number(params.id);
  const topics = props.topics;
  let _title = null;
  let _body = null;
  for(let i=0; i<topics.length; i++){
    if(topics[i].id === id){
      _title = topics[i].title;
      _body = topics[i].body;
    }
  }
  const [title, setTitle] = useState(_title);
  const [body, setBody] = useState(_body);
  function submitHandler(evt){
    evt.preventDefault();
    let title = evt.target.title.value;
    let body = evt.target.body.value;
    props.onSubmit(id, title, body)
    console.log(title);
  }
  return <article>
    <h2>Update</h2>
    <form onSubmit={submitHandler}>
      <p><input type="text" name="title" placeholder="title" value={title} onChange={evt=>setTitle(evt.target.value)} /></p>
      <p><textarea name="body" placeholder="body" value={body} onChange={evt=>setBody(evt.target.value)}></textarea></p>
      <p><input type="submit" value="update" /></p>
    </form>
  </article>
}
function App() {
  console.log('App');
  const [mode, setMode] = useState('WELCOME');
  const [id, setId] = useState(null);
  const navigate = useNavigate();
  console.log('id', id);
  // const [nextId, setNextId] = useState(4);
  const nextId = useSelector(state=>state.nextId);
  // 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 topics = useSelector(state=>state.topics);
  const dispatch = useDispatch();
  function changeModeHandler(id){

    // let newTopics = [];
    // for(let i=0; i<topics.length; i++){
    //   if(topics[i].id !== id){
    //     newTopics.push(topics[i]);
    //   }
    // }
    // setTopics(newTopics); todo
    dispatch({type:'DELETE', id:id});
    navigate('/');
    return;

  }

  return (
    <>
      <Header title="WEB" onChangeMode={changeModeHandler}/>
      <Nav data={topics} onChangeMode={changeModeHandler}/>
      <Routes>
        <Route path="/" element={<Article title="Welcome" body="Hello, React!"/>}></Route>
        <Route path="/read/:id" element={<Read topics={topics}></Read>}></Route>
        <Route path="/create" element={
          <Create onSubmit={(_title, _body)=>{
            // let newTopic = {id:nextId, title:_title, body:_body}
            // let newTopics = [...topics];
            // newTopics.push(newTopic);
            // setTopics(newTopics); todo
            // setMode('READ');
            // setId(nextId);
            dispatch({type:'CREATE', title:_title, body:_body});
            navigate('/read/'+nextId);
            // setNextId(nextId+1);
            dispatch({type:'INCREASE_NEXT_ID'});
          }}></Create>
        }></Route>
        <Route path="/update/:id" element={<Update topics={topics} onSubmit={(id, title, body)=>{
          // let newTopics = [...topics];
          // for(let i=0; i<newTopics.length; i++){
          //   if(newTopics[i].id === id){
          //     newTopics[i].title = title;
          //     newTopics[i].body = body;
          //   }
          // }
          // setTopics(newTopics); todo
          dispatch({type:'UPDATE', id:id, title:title, body:body});
          navigate('/read/'+id);
    }}></Update>}></Route>
      </Routes>

      <Routes>
        <Route path="/" element={<Control />}></Route>
        <Route path="/read/:id" element={<Control onDelete={changeModeHandler} />}></Route>
      </Routes>
    </>
  );
}
function Read(props){
  const params = useParams();
  const id = Number(params.id);
  const topics = props.topics;
  let title = null;
  let body = null;
  for(let i=0; i<topics.length; i++){
    if(topics[i].id === id){
      title = topics[i].title;
      body = topics[i].body;
    }
  }
  return <Article title={title} body={body}/>
}
function Control(props){
  const params = useParams();
  const selectedId = Number(params.id);
  let contextUI = null;
  if(selectedId>0){
    contextUI = <>
      <li>
        <Link to={'/update/'+selectedId}>update</Link>
      </li>
      <li>
        <form onSubmit={evt=>{
          evt.preventDefault();
          props.onDelete(selectedId);
        }}>
          <input type="submit" value="delete" />
        </form>
      </li>
    </>
  }
  return <ul>
    <li><Link to="/create">create</Link></li>
    {contextUI}
  </ul>
}
export default App;

 

📝 테스트

  • Test Driven Development : TDD
  • 요즘은 테스트 코드를 작성하고 코드를 작성한다.
  • 생산성에 저하가 올 수 있지만 견고하고 탄탄한 코드를 작성할 수 있다.
  • Unit test
    • 하나의 모듈을 기준으로 독립적으로 동작하는 가장 작은 단위의 테스트 → React Component
    • 어떤 컴포넌트 기능이 하나 있다면 그 기능에 대응하는 테스트 하나를 만들게 된다.
  • Integration Test
    • 전체 테스트
    • 호환성을 위해서 하는 테스트

 

💡 오늘 깨달은 것

  • 실시간 수업, 온라인 수업, 문제 등 버전이 다르다 보니 너무 헷갈린다. 학습은 함수형으로 배우다가 문제는 클래스형으로 나오고...
  • 테스팅은 재밌으면서도 어려워 보인다.

 

📌 참고

 
반응형