이 글은 [[한글자막] React 완벽 가이드 with Redux, Next.js, TypeScript]를 수강하며 정리한 글입니다.

 

 

⚛️ 기본 환경: IDE: VS code, Language: React

 

 

JSX에서 return 값은 1개만 가능하므로 여러 요소들이 return될 때, 1개의 tag로 감싸거나 배열을 선언해야 함(참고)

⭐ 배열 선언보다는 <div>를 통해서 1개의 return값을 만듦

 

🚨 JSX 요구사항에 따른 다수의 <div> 선언에 따른 불필요한 <div> 생성

▶ tag styling에 좋지 않

다수의 html 요소 rendering에 따른 페이지 로딩 속도 저하

 

 

<div> Soup 해결 방법

1. Wrapper.js 생성

1
2
3
4
5
6
const Wrapper = (props) => {
    return props.children;
}
 
export default Wrapper;
 
cs
1
2
3
4
5
6
7
return (
    <Wrapper>
        <h2 key="h2">Hi there!</h2>
        <p key="p">This does not work :-(</p>
    </Wrapper>
);
 
 
cs

⭐ <div>를 따로 선언해주지 않았으므로 태그를 반환하지 않지만, Wrapper Component로 감싸주어 JSX 제약사항은 만족시킴

 

2. React.Fragment 사용(Wrapper component와 동일한 기능)

1
2
3
4
5
6
7
return (
    <React.Fragment>
        <h2 key="h2">Hi there!</h2>
        <p key="p">This does not work :-(</p>
    </React.Fragment>
);
 
 
cs

+ 기능 지원 시, 축약형 사용 가능

1
2
3
4
5
6
7
return (
    <>
        <h2 key="h2">Hi there!</h2>
        <p key="p">This does not work :-(</p>
    </>
);
 
 
cs

 

cf. <React.Fragment> 와 <Fragment>

import React, { useState } from 'react'; ▶ <React.Fragment> 사용 가능

import React, { useState, Fragment } from 'react'; ▶ <React.Fragment>, <Fragment> 사용 가능

 

 

 

참고 자료

 

⚛︎ <div> soup 방지하는 방법

루트 jsx 요소는 1개여야만 한다.jsx가 js로 변환하면서 값 하나만 반환 가능하기 때문이다.그래서 인접한 요소들을 <div>로 감싸게 되는데, 불필요한 div요소가 중첩되어 <div> soup이 발생한다.wrapper

velog.io

 

 

<div> Soup 🥘 해결하기

div soup를 해결하는 세가지 방법에 대해서 다룹니다.

velog.io

 

이 글은 [[한글자막] React 완벽 가이드 with Redux, Next.js, TypeScript]를 수강하며 정리한 글입니다.

 

 

⚛️ 기본 환경: IDE: VS code, Language: React

 

 

JSX return값 1개

1
2
3
4
5
return (
    <h2>Hi there!</h2>
    <p>This does not work :-(</p>
);
 
cs

🚨 Adjacent JSX elements must be wrapped in an enclosing tag

 

 

해결방법

1. Wrapper 태그로 감싸기

1
2
3
4
5
6
7
return (
    <div>
        <h2>Hi there!</h2>
        <p>This does not work :-(</p>
    </div>
);
 
 
cs

⭐ div tag뿐만 아니라 다른 기본 html 태그도 가능하며, component 또한 가능

 

2. 배열 선언

1
2
3
4
5
return [
    <h2 key="h2">Hi there!</h2>
    <p key="p">This does not work :-(</p>
];
 
 
cs

⭐ 배열 선언 시, 효율적인 rendering을 위해 key값 선언 필요

(key값 미 선언 시, 🚨 Warning: Each child in a list should have a unique "key" prop. 경고 발생)

 

⚛️ 기본 환경: IDE: VS code, Language: React

 

 

발생 Error

React에서 다음 Source Code를 실행할 경우,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const App = () => {
  const [courseGoals, setCourseGoals] = useState([
    { text: "Do all exercises!", id: "g1" },
    { text: "Finish the course!", id: "g2" },
  ]);
 
  const addGoalHandler = (enteredText) => {
    setCourseGoals((prevGoals) => {
      const updatedGoals = [...prevGoals];
      updatedGoals.unshift({ text: enteredText, id: "goal1" });
      return updatedGoals;
    });
  };
 
  return (
    <div>
      <section id="goal-form">
        <CourseInput onAddGoal={addGoalHandler} />
      </section>
      <section id="goals">{content}</section>
    </div>
  );
};
 
 
cs
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
const CourseInput = (props) => {
  const [enteredValue, setEnteredValue] = useState('');
  const [isValid, setIsValid] = useState(true);
 
  const formSubmitHandler = (event=> {
    event.preventDefault();
    if (enteredValue.trim().length === 0) {
      setIsValid(false);
      return;
    }
    props.onAddGoal(enteredValue);
  };
 
  return (
    <form onSubmit={formSubmitHandler}>
      <div
        className={`${styles['form-control']} ${!isValid && styles.invalid}`}
      >
        <label>Course Goal</label>
        <input type="text" onChange={goalInputChangeHandler} />
      </div>
      <Button type="submit">Add Goal</Button>
    </form>
  );
};
 
 
cs

🚨 Add Goal 실행 시, 다음과 같은 경고 발생

Warning: Encountered two children with the same key, `goal1`.
Keys should be unique so that components maintain their identity across updates.
Non-unique keys may cause children to be duplicated and/or omitted

 

 

발생 원인

map()을 통해서 반복문을 실행 할 경우, key값을 중복하여 부여함

1
2
3
4
5
6
7
8
  const addGoalHandler = (enteredText) => {
    setCourseGoals((prevGoals) => {
      const updatedGoals = [...prevGoals];
      updatedGoals.unshift({ text: enteredText, id: "goal1" });
      return updatedGoals;
    });
  };
 
 
cs

addGoalHandler로 추가되는 데이터값의 id가 goal1으로 고정되어있으며,

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const CourseGoalList = props => {
  return (
    <ul className="goal-list">
      {props.items.map(goal => (
        <CourseGoalItem
          key={goal.id}
          id={goal.id}
          onDelete={props.onDeleteItem}
        >
          {goal.text}
        </CourseGoalItem>
      ))}
    </ul>
  );
};
 
 
cs

해당 id값을 이용하여 key값을 부여하였으므로 중복값 발생

 

 

해결 방법

중복되지 않은 key값 부여

1
2
3
4
5
6
7
8
  const addGoalHandler = (enteredText) => {
    setCourseGoals((prevGoals) => {
      const updatedGoals = [...prevGoals];
      updatedGoals.unshift({ text: enteredText, id: Math.random().toString() });
      return updatedGoals;
    });
  };
 
 
cs

 

이 글은 [[한글자막] React 완벽 가이드 with Redux, Next.js, TypeScript]를 수강하며 정리한 글입니다.

 

 

⚛️ 기본 환경: IDE: VS code, Language: React

 

 

 

 

1. Setting Dynamic Inline Styling

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  return (
    <form onSubmit={formSubmitHandler}>
      <div className="form-control">
        <label style={{ color: !isValid ? "red" : "black" }}>Course Goal</label>
        <input
          style={{ border: !isValid ? "1px solid red" : "1px solid #CCC" }}
          type="text"
          onChange={goalInputChangeHandler}
        />
      </div>
      <Button type="submit">Add Goal</Button>
    </form>
  );
 
 
cs

 

2. Setting CSS Classes Dynamically

1
2
3
4
5
6
7
8
9
10
  return (
    <form onSubmit={formSubmitHandler}>
      <div className={`form-control ${!isValid ? 'invalid' : ''}`}>
        <label>Course Goal</label>
        <input type="text" onChange={goalInputChangeHandler} />
      </div>
      <Button type="submit">Add Goal</Button>
    </form>
  );
 
 
cs
1
2
3
4
5
6
7
8
9
.form-control.invalid input {
  border-color: salmon;
  background-color: #ffd7d7;
}
 
.form-control.form-control.invalid label {
  color: salmon;
}
 
 
cs

 

3. Using Styled Library1

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
const FormControl = styled.div`
   {
    margin: 0.5rem 0;
  }
 
  & label {
    font-weight: bold;
    display: block;
    margin-bottom: 0.5rem;
  }
 
  & input {
    display: block;
    width: 100%;
    border: 1px solid #ccc;
    font: inherit;
    line-height: 1.5rem;
    padding: 0 0.25rem;
  }
 
  & input:focus {
    /*input:focus, input type에 focus가 들어왔을 때*/
    outline: none;
    background: #fad0ec;
    border-color: #8b005d;
  }
 
  &.invalid input {
    border-color: salmon;
    background-color: #ffd7d7;
  }
 
  &.invalid label {
    color: salmon;
  }
`;
 
 
const CourseInput = (props) => {
  return (
    <form onSubmit={formSubmitHandler}>
      <FormControl className={!isValid && 'inValid'}>
        <label>Course Goal</label>
        <input type="text" onChange={goalInputChangeHandler} />
      </FormControl>
      <Button type="submit">Add Goal</Button>
    </form>
  );
};
 
 
cs

 

4. Using Styled Library2

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const FormControl = styled.div`
   {
    margin: 0.5rem 0;
  }
 
  & label {
    font-weight: bold;
    display: block;
    margin-bottom: 0.5rem;
    color: ${props => props.invalid ? 'tomato' : 'black'};
  }
 
  & input {
    display: block;
    width: 100%;
    border: 1px solid ${props => (props.invalid ? 'tomato' : '#ccc')};
    background: ${props => props.invalid ? '#ffd7d7' : 'transparent'};
    font: inherit;
    line-height: 1.5rem;
    padding: 0 0.25rem;
  }
 
  & input:focus {
    /*input:focus, input type에 focus가 들어왔을 때*/
    outline: none;
    background: #fad0ec;
    border-color: #8b005d;
  }
 
`;
 
 
const CourseInput = (props) => {
  return (
    <form onSubmit={formSubmitHandler}>
      <FormControl invalid={!isValid}>
        <label>Course Goal</label>
        <input type="text" onChange={goalInputChangeHandler} />
      </FormControl>
      <Button type="submit">Add Goal</Button>
    </form>
  );
};
 
 
cs

 

5. Using CSS Module

1
2
3
4
5
6
7
8
9
10
11
12
  return (
    <form onSubmit={formSubmitHandler}>      <div className={`${styles['form-control']} ${!isValid && styles.invalid}`}>
      {/* className에 -가 들어가는 경우, ''로 감싼 후 []로 감싸기
      styles['form-control']: form-control이라는 클래스를 styles 객체에서 가져오기 → output: form-control
      !isValid && styles.invalid: isValued 초기값-true, && 첫 번째 피연산자가 false로 평가되는 경우 그 값을 반환 → output: inValid
       */}
        <label>Course Goal</label>
        <input type="text" onChange={goalInputChangeHandler} />
      </div>
      <Button type="submit">Add Goal</Button>
    </form>
  );
cs

 

이 글은 [[한글자막] React 완벽 가이드 with Redux, Next.js, TypeScript]를 수강하며 정리한 글입니다.

 

 

⚛️ 기본 환경: IDE: VS code, Language: React

 

 

1. For ... in ...: 객체에서 사용 권장

: 순서없이 반복되어 배열의 인덱스 순서대로 반복문이 진행된다는 보장이 되지 않음

: 객체의 모든 열거 가능한 속성(key, value)을 반복할 수 있음

 

2. For ... of ...: 배열 등 Collection에서 사용 권장

: 배열의 인덱스 순수대로 반복문 진행 보장

: of 앞에는 index number가 아닌 배열의 요소를 나타냄

▶ 배열의 0번째 요소를 출력하고 싶으면, arr[0]이 아닌 0(element)를 직접적으로 입력해야 함

 

 

(왼쪽) for-in / (오른쪽) for-of

 

 

 

참고 자료

 

[Javascript] 반복문 for.. in 과 for.. of 의 차이

지난 포스팅에서 배열이 내장함수인 forEach()에 대한 기초적인 내용들을 정리를 해보았습니다. forEach, for...in을 이용한 반복문 기초(ft. 배열, 객체 Data 뽑기) forEach, for...in을 이용한 반복문 기초(ft.

kincoding.com