웹풀스택 공부 중
12 - 1. React 문법 본문
유용한 함수들
.map
: 하나의 연산을 모든 element에다가 적용할 수 있다array.map((each) => each * 10)
이런식으로 각각의 element에 10을 곱할 수 있다foreach
는?foreach
는 반환값이 없다. 그저 연산을 위함이다
.filter
: 특정 조건에 따라 표시할지 말지 결정한다array.filter((each) => each.attribute === value)
- ex.)
const filteredBooks = books.filter((book) => book.title.include(keyword))
.reduce
: element의 수를 순차적으로 줄여가며 값을 만든다필요한것은 두개: 초기값, 단일 element에 대한 연산 함수
array.reduce((previous, each) => previous + each, 0)
ex.)
// 방법 1 const numbers = [1,2,3,4,5,6,7,8,9,10] const sum1 = numbers.reduce((accumulator, currNumber) => accumulator + currNumber) // 방법 2 function sumReducer(accumulator, currNumber){ return accumulator + currNumber } const sum2 = numbers.reduce(sumReducer)
- React Function에서 props로 넘길때는 무조건
props
로 넘겨야한다.function OrderedList(props){…}
- 아니면 children으로
{...}
를 통해 쪼게서 넣어도 된다function OrderedList({items}){...}
State Lifting (상태 끌어올리기)
자식 컴포넌트가 부모 컴포넌트의 State를 SetState로 변경하는 행위
- 부모 컴포넌트가 가지고 있는 상태 변경을 위한 SetState를 자식 컴포넌트가 호출이 가능하다
- React는 데이터의 흐름이 단방향이므로 자식 컴포넌트의 변수를 부모 컴포넌트로 끌어올려주게 된다면 같은 부모 컴포넌트를 둔 다른 자식 컴포넌트들 간의 변수 공유가 가능해진다
- 예시를 통해 배워보자
function App() {
return (
<>
<Counter/>
<Counter/>
</>
}
function Counter(){
return (
<>
<button onClick={() => { setCount ((prev => prev + 1))}> </button>
</>
- 일때, App()에서 결과값을 보여주고 싶다면, State Lifting을 통해 결과값을 전달할 수 있다
function App(){
const [count, setCount] = useState(0)
const handleClick = () => setCount((prev) => prev + 1)
return (
<div>
<Counter total={count} onClick={handleClick}/>
<Counter total={count} onClick={handleClick}/>
</div>
)}
function Counter({total, onClick}){
const [count, setCount] = useState(0)
return (
<div>
{count} {total}
<button onClick = {()=> {setCount((prev) => prev + 1) onClick()}>
...
- 이런식으로 state와 props, callback function을 이용해서 State Lifting이 가능하다
Ref (외부 변수) - 비제어 컴포넌트
비제어 Component: React가 인지하지 못하는 변수 이기에, Ref 내부 값이 변경될 시, Rerendering 하지않는다!
Ref를 통해 값을 관리하는 Component를 비제어 컴포넌트라고 부른다
언제 사용하는가?
- 너무 많은 입력 form이 있어, 각 form 입력에 따른 유효성 검사 시 너무 많은 수 의 Rerender가 발생할때
- UI와 직접적으로 관련이 없고, 복잡한 형태의 데이터라서 State를 사용하고 싶지 않을때
사용하는 이유?
- 불필요한 Rerendering 횟수 감소
- 순수 HTML 요소 접근/조작
- React Component가 아니라 내부 상태가 존재하지 않는 순수 HTML 요소를 뜻한다
무엇을 할 수 있는가?
특정 요소에 포커스 주기
import { useRef } from 'react' function Field() { const inputRef = useRef(); function handleFocus() { inputRef.current.focus(); } return ( <> <input type="text" ref={inputRef} /> <button onClick={handleFocus}>입력란 포커스</button> </> ) }
특정 요소에 Utterances를 붙이기
const Comments = () => { const commentRef = useRef(null); useEffect(() => { // script element 생성 const utterances = document.createElement('script'); // attribute를 전체를 객체로 만들기 const utterancesConfig = { src: 'https://utteranc.es/client.js', repo: 'user/repo', theme: '선택한 테마', 'issue-term': '포스트 페이지 매핑 방법', async: true, crossorigin: 'anonymous', }; // 객체 전체를 setAttribute로 붙이기 Object.entries(utterancesConfig).forEach(([key, value]) => { utterances.setAttribute(key, value); }); // 만든 script를 ref 항목에 appendChild로 붙이기 commentRef.current.appendChild(utterances); }, []); return <div ref={commentRef} />; }; export default Comments;
특정 요소에 Observer 붙일 때
- 스크롤에 따라 상단에 제목을 포기할지 말지
- Obeserver - 객체이다
- 사용방법
- Define
- Attach
- Detach (free memory)
- 사용방법
import { useRef, useEffect } from 'react' function SpyExample() { const spy = useRef() useEffect(() => { // 1. Define const observer = new window.IntersectionObserver(([entry]) => { if (!entry.intersectionRatio) { document.getElementById('header-title').classList.add('scrolled-a-bit') } else { document.getElementById('header-title').classList.remove('scrolled-a-bit') } }) // 2. Attach observer.observe(spy.current) // 3. Detach return () => { observer.disconnect() } }, []) return ( <div ref={spy} /> ) }
사용방법
useRef(null)
: initialize 할때 무조건 null을 줘야한다!- TS에서는 요소를 쓸 것인지, HTML Input인지 타입을 명시할 수 있다
- 하지만 JS에서는
null
을 쓰면 알아서 잘 찾아준다!null
을 사용할때 주의점:- Ref 값을 주기전까지 사용할 수 없다!
useEffect
를 통해 사용할 수 있다 ← 이후 다룰예정?
를 통해 사용 할 수 도 있다 : Optical Chaining- ex.)
countRef.current?.value
countRef.current?.value ?? ...
로 default value를 표시할 수도 있다
- runtime에서 null guard 수단이다
!
도 null gaurd 중 하나지만 개발자 래벨에서 확실하게 보증해준다
- ex.)
Validation 문법
&&
(AND) : 앞에 있는 값이 TRUE 일때 뒤에 것을 표시하라||
(OR): 앞에 있는 값이 FALSE라면 뒤에 것을 표시하라
import { useRef, useState } from 'react'
import './App.css'
//Validator
function App() {
const nameRef = useRef(null)
// View가 변경되려면 State가 필요하다!
const [lengthValidate, setLengthValidate] = useState(false)
const [requiredValidate, setRequiredValidate] = useState(false)
return (
<>
<div>이름: <input ref={nameRef} onChange={(e)=>{
const input = e.currentTarget.value
setLengthValidate(input.length > 10)
setRequiredValidate(input.length > 0)
}}/></div>
{lengthValidate || <div style={{color:'red'}}> - 에러 메세지: name cannot be longer than 10</div> }
{requiredValidate || <div style={{color:'red'}}> - 에러 메세지: name is required</div>}
</>
)
}
export default App
import { useRef, useState } from 'react'
import './App.css'
function FormWithValidation({ label, refe, required = false, length }) {
// const nameReference = useRef(null)
const [lengthValid, setLengthValid] = useState(true)
const [requiredValid, setRequiredValid] = useState(true)
console.log('- rerendered : ' + label)
return (
<>
<div>
{label} :{' '}
<input
ref={refe}
onChange={(e) => {
const input = e.currentTarget.value
if (length) setLengthValid(input.length <= length)
if (required) setRequiredValid(input.length > 0)
}}
/>
</div>
{lengthValid || (
<div style={{ color: 'red' }}>
{label}의 길이는 {length}을 넘어선 안됩니다.
</div>
)}
{requiredValid || <div style={{ color: 'red' }}>{label}은 필수 입력값입니다.</div>}
</>
)
}
function App() {
const reference = {
name: useRef(null),
desc: useRef(null),
mail: useRef(null),
}
function registration() {
const response = {
name: reference.name.current?.value,
desc: reference.desc.current?.value,
mail: reference.mail.current?.value,
}
console.log(response)
}
return (
<>
<FormWithValidation label='이름' refe={reference.name} required length={10}/>
<FormWithValidation label='설명' refe={reference.desc} length={20} />
<FormWithValidation label='메일' refe={reference.mail} required length={20} />
<button onClick={registration}>제출</button>
</>
)
}
export default App
- 이런식으로 값을 넘겨서 받을 수 도 있다!
import { useRef, useState } from 'react'
import './App.css'
// useForm 만들어보기
// validation을 비구조화 해서 ref와 validate를 받아온다.
function PasswordInput({validation: {ref, validate}}){
function changeMode(e) {
if (ref.current.type === 'password'){
ref.current.type = 'text'
e.currentTarget.innerText = '🔓보이기'
} else if (ref.current.type === 'text'){
ref.current.type = 'password'
e.currentTarget.innerText = '🔒감추기'
}
}
// 중앙화 1번 - Validate를 묶어줬다
const [valid, setValid] = useState({
maximum: true,
minimum: true,
required: true,
})
return (
<div>
비밀번호 : <input ref={ref} onChange={(e)=>{
const validated = validate(e.currentTarget.value)
setValid(validated)
}}/>
<button onClick={changeMode}>
🔒감추기/보이기</button>
{valid.maximum || <div style = {{color: 'red'}}>비밀번호는 10글자를 넘을 수 없습니다.</div>}
{valid.minimum || <div style = {{color: 'red'}}>비밀번호는 5글자를 넘어야 합니다.</div>}
{valid.required || <div style = {{color: 'red'}}>비밀번호를 입력해주세용.</div>}
</div>
)
}
function UsernameInput(){
return (
<div>
아이디 : <input />
</div>
)
}
function App() {
const validation = {
id: {ref: useRef(null), validate: (input) => true, },
password: {ref: useRef(null), validate: (input) => {return {
maximum: input.length <= 10,
minimum: input.length >= 5,
required: input.length > 0,
}}, },
}
function login(){
// 1.하나라도 무효할때 해당 인풋으로 커서 이동
const password = validation.password.ref.current?.value
const validated = validation.password.validate(password)
// 2. 모두 유효할때 console.log ("제출 완료")
if (!(validated.maximum && validated.minimum && validated.required)){
alert(`
${validated.maximum || '비밀번호는 10글자를 넘을 수 없습니다.'}
${validated.minimum || '비밀번호는 5글자를 넘어야 합니다.'}
${validated.required || '비밀번호를 입력해주세용.'}
`)
}
}
return (
<>
<UsernameInput validation = {validation.id}/>
<PasswordInput validation = {validation.password}/>
<button onClick={login}>Log in</button>
</>
)
}
export default App
- SImple login form
import { useRef, useState } from 'react'
import './App.css'
// useForm 만들어보기
// validation을 비구조화 해서 ref와 validate를 받아온다.
function PasswordInput({validation: {ref, validate}}){
function changeMode(e) {
if (ref.current.type === 'password'){
ref.current.type = 'text'
e.currentTarget.innerText = '🔓보이기'
} else if (ref.current.type === 'text'){
ref.current.type = 'password'
e.currentTarget.innerText = '🔒감추기'
}
}
// 중앙화 1번 - Validate를 묶어줬다
const [valid, setValid] = useState({
maximum: true,
minimum: true,
required: true,
})
return (
<div>
비밀번호 : <input ref={ref} onChange={(e)=>{
const validated = validate(e.currentTarget.value)
setValid(validated)
}}/>
<button onClick={changeMode}>
🔒감추기/보이기</button>
{valid.maximum || <div style = {{color: 'red'}}>비밀번호는 10글자를 넘을 수 없습니다.</div>}
{valid.minimum || <div style = {{color: 'red'}}>비밀번호는 5글자를 넘어야 합니다.</div>}
{valid.required || <div style = {{color: 'red'}}>비밀번호를 입력해주세용.</div>}
</div>
)
}
function UsernameInput(){
return (
<div>
아이디 : <input />
</div>
)
}
function App() {
const validation = {
id: {ref: useRef(null), validate: (input) => true, },
password: {ref: useRef(null), validate(input) {
if (!(input.length <= 10 && input.length > 5 && input.length > 0)){
this.ref.current.focus()
}return {
maximum: input.length <= 10,
minimum: input.length >= 5,
required: input.length > 0,
}}, },
}
function login(){
// 1.하나라도 무효할때 해당 인풋으로 커서 이동
const password = validation.password.ref.current?.value
const validated = validation.password.validate(password)
// 2. 모두 유효할때 console.log ("제출 완료")
if (!(validated.maximum && validated.minimum && validated.required)){
alert(`
${validated.maximum || '비밀번호는 10글자를 넘을 수 없습니다.'}
${validated.minimum || '비밀번호는 5글자를 넘어야 합니다.'}
${validated.required || '비밀번호를 입력해주세용.'}
`)
}
}
return (
<>
<UsernameInput validation = {validation.id}/>
<PasswordInput validation = {validation.password}/>
<button onClick={login}>Log in</button>
</>
)
}
export default App
focus
사용해보기
반응형
'웹개발 > React' 카테고리의 다른 글
13 - 3. React Hook (0) | 2024.10.31 |
---|---|
11 - 1. React (3) | 2024.10.10 |