01. Java
02. git
03. Database
04. Jsp [Server]
05. HTML,CSS
07. JS
06. 미니프로젝트-2W
08. SpringFramework , SrpingBoot
19. 중간프로젝트 (1M)
10. Linux 명령어
11. AWS
12. Docker
13. Kubernetes
14. React JS (v)
15. App - Android
16. 최종프로젝트 (1M)
const 사용할이름 = useRef(초기값)
특정태그에 이름 달기
useRef() 훅을 이용해 특정태그에 이름을 지정하고 핸들링할 수 있음
useRef는 초기값을 객체로 ({current:value}) 저장하기 때문에 변수명.current로 현재태그에 접근할 수 있음
실습
function HookQ(){ /* 1. 이 페이지가 mount된 이후에 useEffect사용해서 id태그에 포커싱을 하나 넣기 2. id, pw는 동시에 state로 관리 3. 로그인 클릭시 두값중 한개라도 공백이라면, 공백인 태그에 포커싱 추가 4. 로그인 클릭시 공백이 아니라면, 경고창으로 id, pw출력 */ const [info,setInfo] = useState({id:'',pw:''}); const idRef=useRef(null); const pwRef=useRef(null); const handleClick = (e) => { if(idRef.current.value===''){ idRef.current.focus(); } else if( pwRef.current.value===''){ pwRef.current.focus(); } else{ alert(`id=${idRef.current.value} , pw=${pwRef.current.value}`) } }; useEffect(()=>{ idRef.current.focus(); },[]) return( <> <div> <input type="text" name="id" onChange={e=>setInfo({...info,id:e.target.value})} value={info.id} placeholder="아이디" ref={idRef} /><br/> <input type="password" name="pw" onChange={e=>setInfo({...info,pw:e.target.value})} value={info.pw}placeholder="비밀번호" ref={pwRef} /><br/> <button type="button" onClick={handleClick}>로그인</button> </div> </> ) }
외부에서 state를 관리할 수 있게 해줌
리듀서함수를 외부로 export하게 되면 로직 분리할 수 있음 => 재사용 가능
리듀서
const [현재값, 리듀서action함수] = useReducer(리듀서함수명, state값);
export const firstReducer = (state,action) =>{ // state,action을 이용해서 state값 변경 // state는 직접 값을 변경하는 작업을 피해서 수정 // 초기 state유형 = {value:0} if(action.type==='up'){ state = {value:state.value+1} }else if(action.type=='down'){ state = {value:state.value-1} }else if(action.type== 'result') { state={value:0} }; } function HookReducer(){ // const [현재스테이트값, 리듀서를 변경하는 acion함수] = useReducer(리더서함수, state초기값) const [state, some] = useReducer(firstReducer,{value:0}); const up = ()=>{ some({type:"up"}); // {type:"up"}은 리듀서의 두번째 매개변수로 전달달 } return( <> <h3>useReducer훅</h3> 현재state값: {state.value} <button type="button" onClick={up}>증가</button> <button type="button" onClick={()=>some({type:"down"})}>감소</button> <button type="button" onClick={()=>some({type:"reset"})}>초기화</button> </> ) }
- some함수안에 객체형태로 데이터를 담아서 보내게 되면 firstReducer의 action에서 그 값을 받음
- 즉 **
dispatch함수
**는 객체 형태의 액션을 reducer에 전달하는 역할 수행- state의 구조는 reducer를 호출하는 컴포넌트에서 명시
const [state, some] = useReducer(firstReducer,{value:0});
부분
useMemo
는 값을 기억해서 필요할때만 다시 계산하도록 도와주는 훅function HookMemo(){ const [num,setNum] = useState(0); const [text,setText] = useState(''); // const calc=(()=>{ // // 계산이 오래걸리는 예시 함수 // console.log("계산시작") // let i=0; // while(i<=1000000000)i++; // console.log("계산완료"); // return num % 2 == 0 ? "짝수":"홀수"; // })(); const calc = useMemo( () => { // 계산이 오래걸리는 예시 함수 console.log("계산시작") let i=0; while(i<=1000000000)i++; console.log("계산완료"); return num % 2 == 0 ? "짝수":"홀수"; },[num]) // num이 변경될때만 실행 return( <> <h1>useMemo훅</h1> <input type="number" onChange={(e)=>setNum(e.target.value)} value={num}/> <br/> 결과:{calc} <br/> <input type="text" onChange={e=>setText(e.target.value)} value={text}/> </> ) }
- 특정 state를 지정하고 해당 state의 값이 변화할때만 useMemo안의 함수 실행
- 현재, num값이 바뀔시 useMemo안의 함수 실행
- 함수는 리렌더링 될때마다 새롭게 만들어짐
const memoizedCallback = useCallback(() => { // 실행할 함수 내용 }, [의존성]);
() => {...}
: 기억할 함수[의존성]
: 배열 안 값이 바뀌지 않으면, 기억한 함수를 그대로 사용함
function HookCallback() { const [count, setCount] = useState(0); const increase = () => { setCount( prev => prev + 1); } return ( <> <h3>useCallback훅</h3> <button type="button" onClick={increase}>버튼</button> <br/> 현재값: {count} <Child increase={increase}/> </> ) } //자식컴포넌트 const Child = ({increase}) => { //자식 컴포넌트에서 상위 컴포넌트의 state값을 변경 console.log("부모에서 클릭하던, 자식에서 클릭하던 렌더링이 일어남"); return ( <> <h3>자식 컴포넌트</h3> <button type="button" onClick={()=>increase()}>증가</button> </> ) }
- 부모 HookCallback컴포넌트에서 만든 increase함수를 Child컴포넌트에 전달해줌
- **
useCallback
**함수를 쓰지 않으면 Child컴포넌트의 증가버튼을 누르게 되면 count state값이 증가하게 되면서 리렌더링 되며 부모컴포넌트의 increase함수가 재생성됨
- increase함수안에서 prev=>prev+1로 쓴이유는 setCount(count+1)로 설정하게 되면 제일 처음 받았던 count가 0인데 이 count에 대해서 계속 +1을 하기때문에 increase함수 호출시 계속 0+1인 1로 count가 설정됨
- **
useCallback
**함수를 써서 Child컴포넌트에서 증가버튼을 눌러 리렌더링시켜도 처음 부모 컴포넌트에서 생성한 increase함수를 재사용
- 디자인 적용 시 {}로 디자인 요소들을 묶어야함
- css속성중 -은 카멜표기법으로 대체 ex) text-algin => textAlign
<p style=>CSS파일로 디자인하기</p>
- css파일 생성시 컴포넌트의 이름을 반드시 포함해서 만들어야 함
- import로 css파일을 가져오면 됨
import "./css/App.css"
- 파일은 컴포넌트명.module.css형식으로 생성
- css파일은 import구문으로 가져옴
- 이 방식은 선택자에 고유한 해시값을 부여함으로 다른파일과 디자인의 중복을 막음
- :global 키워드를 이용해서 전역 선택자 선언이 가능
- 실습(App.js)
import myStyle from "./css/App.module.css"; function App(){ return( <> <Fragment> <section className={myStyle.appWrap}> <div className="title">hello world</div> </section> </Fragment> </> ) }
- 실습(App.module.css)
.appWrap{ background-color:pink; display:flex; justify-content: center; align-items: center; height:100vh; } /* .tilte이라는 이름으로 바로 사용가능 */ :global .title{ color: red; } >> ```
하나의 페이지로 만들어진 어플리케이션
SPA는 html파일을 브라우저 측에서 로드하고 필요한 데이터는 API와 ajax통신을 이용해 처리
브라우저에서 사용자가 상호작용 하면 필요한 부분만 업데이트해서 처리
멀티플랫폼 Android, IOS에 대응하여 웹뷰로 처리하는 목적으로도 사용
단점
- 앱의 규모가 커지면, JS파일도 너무 커져서 로딩이 오래걸리게 됨
- 브라우저에서 랜더링이 완료되기 까지 비어있는 화면이 나옴
- 규모가 큰 어플리케이션은 SSR(서버사이드 렌더링)방식으로 처리
- index.js
import { BrowserRouter } from "react-router-dom"; .... const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <BrowserRouter> <App /> </BrowserRouter> );
- App.js
function App() { return ( <Routes> <Route path='/' element={<Home/>}/> <Route path='/user' element={<User/>}/> <Route path='/info' element={<Info/>}/> </Routes> ); }
- /요청은 Home컴포넌트
- /user요청은 User컴포넌트
- /info요청은 Info컴포넌트