react import package absolute path 하는 방법

Can't resolve module 이라는 에러가 난다면 상대경로가 아닌 절대경로로 하는게 좋다. 상대 경로로 할 경우에는 import module from '../../../과 같이 명시를 해줘야 하니... 절대 경로로 하는 방법은 아래와 같다.

Project의 ROOT (README.md를 보통 만드는)에 가서 .env의 파일을 만들고 NODE_PATH=src/ 다음과 같이 입력하면 src의 아래에 있는 pages, modules 등을 import Module from 'modules/modulename' 으로 입력하면 된다.

만약 적용이 안된다면 재시작을 해주면 정상적으로 반영된다.

react앱에서 데이터는 위에서 아래로 props을 통해 전달되지만 여러 컴포넌트들에 전해줘야 하는 props의 경우에는 context를 이용하면 트리 단계마다 명시적으로 props을 넘겨주지 않아도 많은 컴포넌트가 값을 공유할 수 있다.

global 변수로서 context를 사용하는 경우는 로그인한 유저, 테마, 선호하는 언어 등이 있을 수 있다. 코드는 아래와 같다.

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}

class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}

위와 같이 props으로 넘기는게 아니라 아래와 같이 context를 사용하면 자식에 props을 넘길 필요가 없음

const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() { 
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component { 
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}

context를 사용하면 컴포넌트를 재사용하기가 어려워지므로 꼭 필요할때만 사용해야 한다.

props와 state는 다르다는것을 알고 상황에 알맞게 잘 사용을 해야한다. 두 객체는 모두 렌더링 결과물에 영향을 주는 정보를 갖고 있지만 두객체의 차이는 분명하다. props은 properties의 줄임말로 함수 매개변수처럼 컴포넌트에 전달되는 반면 state는 함수 내에 선언된 변수처럼 컴포넌트 안에서 관리가 된다.

프로젝트를 설계할때는 state가 최소화되도록 작성하는게 좋다. 중복으로 작성하면 UI의 상호작용에 혼란을 줄수 있다.
일단 state가 변경되면 컴포넌트는 렌더링을 다시 시도한다. 아래와 같은 앱을 만들때 어떤 값을 props으로 사용하고, 어떤 값을 state로 사용하는지 생각을 해보자

  • 부동산의 목록
  • 유저가 입력한 검색어
  • 체크박스의 값
  • 필터링 된 부동산 목록

데이터에 대해 아래의 세 가지 질문을 통해 결정이 가능하다.

  1. 부모로부터 props 을 통해 전달이 되는지?
  2. 시간이 지나도 변하지 않는지
  3. 컴포넌트 안의 다른 state나 props을 가지고 계산하는지

위 세가지에 해당되면 props 이다.
부동산의 목록은 props을 통해 전달되기 때문에 props이고, 검색어와 체크박스는 시간이 지남에 따라 변하기 때문에 state의 값으로 상태값을 업데이트 해야 한다. 필터링 된 제품의 목록은 컴포넌트 안의 검색어, 체크박스의 값으로 계산하기 때문에 props이다.

한페이지에 여러 컴포넌트를 넣고 싶은 경우가 있다. 예를 들어서 왼쪽은 전화번호부 오른쪽은 채팅창! 그럴때는 아래와 같이 코드를 작성하면 된다.

function SplitPane(props) {
  return (
    <div className="SplitPane">
      <div className="SplitPane-left">
        {props.left}
      </div>
      <div className="SplitPane-right">
        {props.right}
      </div>
    </div>
  );
}

function App() {
  return (
    <SplitPane
      left={
        <Contacts />
      }
      right={
        <Chat />
      } />
  );
}

위처럼 작성하면 App에서 왼쪽은 Contacts, 오른쪽은 Chat의 컴포넌트가 화면에 렌더링 된다.

const scaleNames = {
  c: 'Celsius',
  f: 'Fahrenheit'
};

HTML 폼 element는 자체가 내부 상태를 가지기 때문에 React에서의 다른 element와는 조금 다르게 동작한다. 입력폼의 element를 만들고 입력받기 위해서는 아래와 같이 코드를 작성

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

html에서의 textarea는 자식에 값을 설정하지만 react에서 textarea는 attribute에 값을 설정

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'Please write an essay about your favorite DOM element.'
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('An essay was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Essay:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

select의 경우는 아래와 같이 작성

class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('Your favorite flavor is: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Pick your favorite flavor:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">Lime</option>
            <option value="coconut">Coconut</option>
            <option value="mango">Mango</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

checkbox를 사용할때는 아래와 같이 사용

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          Is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

key는 element의 리스트를 사용할때 꼭 명시해줘야 한다. 아래 소스코드를 참고하면 

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

위에서 li의 element에서 key를 사용하지 않으면 경고가 표시된다. 

React에서 Key의 역할은 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕는다. 그렇기 때문에 리스트 element에서 값의 업데이트를 위해서는 반드시 적어줘야 하는데, 이때 각각 element는 고유한 key를 갖고 있어야 하고, 보통은 데이터의 id를 사용한다. 

id가 없다면 최후의 수단으로 항목의 index를 키로 사용할 수 있는데, 이렇게 사용하면 성능이 저하되거나 컴포넌트의 state와 관련한 문제가 발생할 수 있으니 명시적으로 id를 적어주는게 좋다.

위에서 index를 지정하지 않으면 경고가 뜬다고 했는데, 그 이유도 기본으로 index를 key로 사용하기 때문에 표시되는 방법이다. (권장하지 않는다는 뜻!)

컴포넌트 추출하기

컴포넌트로 Blog를 추출하고 리스트의 값을 표시하기 위해서는 아래와 같이 코드를 작성하면 된다.

function ListItem(props) {  
  return <li>{props.value}</li>;
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) => 
    <ListItem key={number.toString()}
              value={number} />

  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);
function Blog(props) {
  const sidebar = (
    <ul>
      {props.posts.map((post) =>
        <li key={post.id}>
          {post.title}
        </li>
      )}
    </ul>
  );
  const content = props.posts.map((post) =>
    <div key={post.id}>
      <h3>{post.title}</h3>
      <p>{post.content}</p>
    </div>
  );
  return (
    <div>
      {sidebar}
      <hr />
      {content}
    </div>
  );
}

const posts = [
  {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
  {id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
  <Blog posts={posts} />,
  document.getElementById('root')
);
 

javascript에서 map을 이용해 list의 값을 순차적으로 사용할 수 있음

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);

JSX에서도 map을 이용해 아래와 같이 사용이 가능, element 모음을 만들고 중괄호를 이용해야 한다.

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li>{number}</li>
);

리스트의 값을 화면에 렌더링하기 위한 컴포넌트를 생성할때는 아래와 같이 사용이 가능하다. 이때 주의해야할 element list를 만들때는 element의 key가 반드시 필요하다. (이때 key는 고유성을 부여하기 위해서 고유하게 식별할 수 있는 문자열을 사용해야 한다. 보통 데이터의 ID를 key로 사용)

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

 

+ Recent posts