반응형
Notice
Recent Posts
Recent Comments
Link
«   2025/09   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Archives
Today
Total
관리 메뉴

웹풀스택 공부 중

12 - 1. React 문법 본문

웹개발/React

12 - 1. React 문법

lukeit 2024. 10. 10. 17:45

유용한 함수들

  • .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 - 객체이다
        • 사용방법
          1. Define
          2. Attach
          3. 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 중 하나지만 개발자 래벨에서 확실하게 보증해준다

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