Dev

React 공부정리 #5

September 7, 2016

React 공부정리 #5

App Overview

  • Udemy 강좌의 ‘Section 5: Redux with React’에 해당하는 내용
  • Redux에 대한 자세한 내용은 Redux Framework를 참고
  • 한글로 번역된 내용은 Dobbit.Co님의 github을 참고

완성한 모습


Component Setup

container\search_bar.js

import React, { Component } from 'react';

export default class SearchBar extends Component {  
    render() {
        return (
            <form className="input-group">
                <input />
                <span className="input-group-btn">
                    <button type="submit" className="btn btn-secondary">Submit</button>
                </span>
            </form>
        );
    }
}
  • containerredux와 연결하기 위한 상위 컴포넌트로 입력값을 전달

components\app.js

import React from 'react';  
import { Component } from 'react';

import SearchBar from '../containers/search_bar';

export default class App extends Component {  
  render() {
    return (
      <div>
        <SearchBar />
      </div>
    );
  }
}
  • component는 하위 컴포넌트로 container를 통해 전달된 값을 화면에 표현하는 것이 목적

Controlled Components and Binding Context

containers\search_bar.js

import React, { Component } from 'react';

export default class SearchBar extends Component {

    constructor(props) {
        super(props);
        this.state = { term: '' };
        this.onInputChange = this.onInputChange.bind(this);
    }

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

    onFormSubmit(event) {
        event.preventDefault();
    }

    render() {
        return (
            <form onSubmit={this.onFormSubmit} className="input-group">
                <input
                    placeholder="Get a five-day forecast in your favorite cities"
                    className="form-control"
                    value={this.state.term}
                    onChange={this.onInputChange} />
                <span className="input-group-btn">
                    <button type="submit" className="btn btn-secondary">Submit</button>
                </span>
            </form>
        );
    }
}

actions\index.js

const API_KEY = '1234567890abcdef1234567890abcdef';  
  • OpenWeatherMap에 가입하고 발급받은 API 키를 입력한다.

Middleware

npm install redux-promise --save  
npm install axios --save  
  • Promise를 사용하기 위해서 Redux 관련 라이브러리와 Promise 기반 HTTP 클라이언트 라이브러리를 설치

src\index.js

import React from 'react';  
import ReactDOM from 'react-dom';  
import { Provider } from 'react-redux';  
import { createStore, applyMiddleware } from 'redux';  
import ReduxPromise from 'redux-promise';

import App from './components/app';  
import reducers from './reducers';

const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);

ReactDOM.render(  
  <Provider store={createStoreWithMiddleware(reducers)}>
    <App />
  </Provider>
  , document.querySelector('.container'));
  • Redux의 Promise를 미들웨어(Middleware)로 생성하고 storePromise를 할당

actions\index.js

import axios from 'axios';

const API_KEY = '8532ab9dab1470c8bff4c8f71d0f089b';  
const ROOT_URL = `http://api.openweathermap.org/data/2.5/forecast?appid=${API_KEY}`;

export const FETCH_WEATHER = 'FETCH_WEATHER';

export function fetchWeather(city) {

    const url = `${ROOT_URL}&q=${city},us`
    const request = axios.get(url);

    return {
        type: FETCH_WEATHER,
        payload: request
    };
}
  • Promise 기반 HTTP 클라이언트를 사용해서 Query에 해당하는 결과를 가져옴

containers\search_bar.js

import React, { Component } from 'react';  
import { connect } from 'react-redux';  
import { bindActionCreators } from 'redux';  
import { fetchWeather } from '../actions/index';

export default class SearchBar extends Component {

    constructor(props) {
        super(props);
        this.state = { term: '' };
        this.onInputChange = this.onInputChange.bind(this);
        this.onFormSubmit = this.onFormSubmit.bind(this);
    }

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

    onFormSubmit(event) {
        event.preventDefault();
        this.props.fetchWeather(this.state.term);
        this.setState({term: ''});
    }

    render() {
        return (
            <form onSubmit={this.onFormSubmit} className="input-group">
                <input
                    placeholder="Get a five-day forecast in your favorite cities"
                    className="form-control"
                    value={this.state.term}
                    onChange={this.onInputChange} />
                <span className="input-group-btn">
                    <button type="submit" className="btn btn-secondary">Submit</button>
                </span>
            </form>
        );
    }
}

function mapDispatchToProps(dispatch) {  
    return bindActionCreators( {fetchWeather}, dispatch);
}

export default connect(null, mapDispatchToProps)(SearchBar);  
  • container에 Query 결과를 바인딩 할 수 있도록 모듈을 추가

Avoiding State Mutations in Reducers

reducers\reducer_weather.js

import { FETCH_WEATHER } from '../actions/index';  
export default function(state = [], action) {  
    switch (action.type) {
    case FETCH_WEATHER:
        return [ action.payload.data, ...state ]
    }
    return state;
}
  • 날씨 정보를 전달할 reducer를 생성

Building a List Container

container\weather_list.js

import React, { Component } from 'react';  
import { connect } from 'react-redux';


class WeatherList extends Component {  
    render() {
        return (
            <table className="table table-hover">
                <thead>
                    <tr>
                        <th>City</th>
                        <th>Temperatures</th>
                        <th>Pressure</th>
                        <th>Humidity</th>
                    </tr>
                </thead>
                <tbody>                
                </tbody>
            </table>
        );
    }
}

function mapStateToProps({weather}) {  
    return { weather };
}

export default connect(mapStateToProps)(WeatherList);
  • 온도, 습도 등을 나타낼 컴포넌트를 생성

Mapping Props to a Render Helper

npm install --save react-sparklines  
npm install --save lodash  
  • 차트를 생성하기 위한 라이브러리를 추가

components\chart.js

import _ from 'lodash';  
import React, { Component } from 'react';  
import { Sparklines, SparklinesLine, SparklinesReferenceLine } from 'react-sparklines';

function average(data) {  
    return _.round(_.sum(data)/data.length);
}

export default (props) => {  
    return (
        <div>
            <Sparklines height={120} width={180} data={props.data}>
                <SparklinesLine color={props.color} />
                <SparklinesReferenceLine type="avg" />
            </Sparklines>
            <div>{average(props.data)} {props.units}</div>
        </div>
    );
}
  • 차트 컴포넌트를 만듬

components\weather_list.js

import React, { Component } from 'react';  
import { connect } from 'react-redux';  
import Chart from '../components/chart';


class WeatherList extends Component {

    renderWeather(cityData) {

        const name = cityData.city.name;
        const temps = cityData.list.map(weather => weather.main.temp);
        const pressures = cityData.list.map(weather => weather.main.pressure);
        const humidities = cityData.list.map(weather => weather.main.humidity);

        return (
            <tr key={name}>
                <td>{name}</td>
                <td><Chart data={temps} color="orange" units="K" /></td>
                <td><Chart data={pressures} color="green" units="hPa" /></td>
                <td><Chart data={humidities} color="black" units="%" /></td>
            </tr>
        );
    }

    render() {
        return (
            <table className="table table-hover">
                <thead>
                    <tr>
                        <th>City</th>
                        <th>Temperatures</th>
                        <th>Pressure</th>
                        <th>Humidity</th>
                    </tr>
                </thead>
                <tbody>
                    {this.props.weather.map(this.renderWeather)}                
                </tbody>
            </table>
        );
    }
}

function mapStateToProps({weather}) {  
    return { weather };
}

export default connect(mapStateToProps)(WeatherList);

Google Map

npm install --save react-google-maps  
  • 구글 지도를 사용하기 위한 리액트 라이브러리를 설치

components\google_maps.js

import React from 'react';  
import { GoogleMapLoader, GoogleMap } from 'react-google-maps';

export default (props) => {  
  return (
    <GoogleMapLoader
      containerElement={ <div style={{height: '100%'}} /> }
      googleMapElement={
        <GoogleMap defaultZoom={12} defaultCenter={{lat: props.lat, lng: props.lon}} />
      }
      />
  );
}
  • 구글 지도를 사용해서 도시 위치를 표시하기 위한 컴포넌트를 만듬

components\weather_list.js

import React, { Component } from 'react';  
import { connect } from 'react-redux';  
import Chart from '../components/chart';  
import GoogleMap from '../components/google_map';

class WeatherList extends Component {

    renderWeather(cityData) {

        const name = cityData.city.name;
        const temps = cityData.list.map(weather => weather.main.temp);
        const pressures = cityData.list.map(weather => weather.main.pressure);
        const humidities = cityData.list.map(weather => weather.main.humidity);
        const { lon, lat } = cityData.city.coord;

        return (
            <tr key={name}>
                <td><GoogleMap lon={lon} lat={lat} /></td>
                <td><Chart data={temps} color="orange" units="K" /></td>
                <td><Chart data={pressures} color="green" units="hPa" /></td>
                <td><Chart data={humidities} color="black" units="%" /></td>
            </tr>
        );
    }

    render() {
        return (
            <table className="table table-hover">
                <thead>
                    <tr>
                        <th>City</th>
                        <th>Temperatures</th>
                        <th>Pressure</th>
                        <th>Humidity</th>
                    </tr>
                </thead>
                <tbody>
                    {this.props.weather.map(this.renderWeather)}                
                </tbody>
            </table>
        );
    }
}

function mapStateToProps({weather}) {  
    return { weather };
}

export default connect(mapStateToProps)(WeatherList);
  • 구글 지도 컴포넌트를 추가
Array