iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 23
1
Modern Web

React 30天系列 第 23

Day 23-使用redux-thunk處理News API

前情提要:
昨天暫時把todos告一段落,今天要開始新建news內容,計畫很簡單就是使用News API取得新聞內容。
剛好這個API很貼心可以選擇國家,所以就想說透過這個API可以看個各國新聞,這次訂定了台灣、日本和美國當作選項
剛好有機會再使用到middleware,這次就使用redux-thunk處理吧。

目標說明

  1. 使用redux-thunk處理News API
  2. 依據使用者選擇的國家取得不同的新聞
  3. 顯示新聞列表
  4. 取得資料時有loading畫面

redux-thunk安裝設定

yarn add redux-thunk

安裝完後再次打開store/index.js,接著依照redux-thunk的步驟匯入applyMiddleware和redux-thunk。接著在createStore設定使用thunk,這樣就設定完成囉!

import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";

import rootReducer from "../reducers";

const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

export default store;

redux部分相關設定
接著是在reducers目錄下新增news_reducer.js處理有關news的state,初始化state的資料內預計會有articlescountry,儲存新聞資料和選擇哪個國家的新聞,分別設定初始值為陣列與空字串。

export default function(state = {
  articles: [],
  country: ""
}, action) {
  // ......
  return state
}

在reducers/index.js也別忘了在combineReducers匯入newsReducer呦!

// ...
import newsReducer from './news_reducer';

const rootReducer = combineReducers({
  todoList: todoListReducer,
  news: newsReducer
});
// ...

前置作業完成後就可以寫UI了
https://ithelp.ithome.com.tw/upload/images/20181030/20111595gKLSeYjzrO.png
UI部分拆成三部分處理,第一個是選擇新聞來源國家的NewsSelect,再來是新聞清單的NewsList,接著是渲染每則新聞的NewsListItem,這三個component都屬於Presentational component。透過最外層的Container component(News component)和redux溝通。

傳入NewsSelect的props分別為country和fetchNews,會將country存在store的原因是因為希望切換route後再回來能保留value。

<NewsSelect fetchNews={fetchNews} country={country} />

透過redux-thunk處理fetchNews。一般的action都是直接return object,而透過redux-thunk可以return function,而function內會收到dispatchgetState作為參數,等收到object後再交給reducer處理。

fetchNews這裡用了三個dispatch。第一次用在更新isLoading和country state,收到response後再依據success或fail發送不同action

export const fetchNews = country => {
  const URL = `https://newsapi.org/v2/top-headlines?country=${country}&apiKey=bb126a5312f5417c852cd93ce738a8f2`;
  return dispatch => {
    dispatch({ type: FETCH_NEWS_REQUEST, country });
    axios
      .get(URL)
      .then(res => dispatch({ type: FETCH_NEWS_SUCCESS, res }))
      .catch(err => dispatch({ type: FETCH_NEWS_ERROR, err }));
  }
};

在news_reducer.js這邊也依據action type做不同處理

switch (action.type) {
  case FETCH_NEWS_SUCCESS:
    let { data:{articles}, status } = action.res;
    return status === 200 ? { ...state, articles, isLoading: false} : state;
  case FETCH_NEWS_ERROR:
    return { err: 'Something Wrong...', isLoading: false }
  case FETCH_NEWS_REQUEST:
    return { ...state, isLoading: true, country: action.country }
  default:
    return state;
}

收到state的News component再透過props將資料傳給NewsList,若收到err則顯示錯誤訊息,下面會看到props除了acticles外還有isLoading和pageUrl也送進去,isLoading就是之前做過的簡單HOC,只是這邊用了react-fontawesome做Loading顯示,另一個pathname是因為之後Link component有需要使用所以先丟進去,但寫到這裡又覺得不用這麼麻煩也行,pathname的部分可能我還需要再想想...

// ...
const News = ({ err, articles, fetchNews, country, isLoading, location }) => {
  return (
    <main className="main">
      <NewsSelect fetchNews={fetchNews} country={country} />
      {err ? (
        <div>{err}</div>
      ) : (
        <NewsList
          articles={articles}
          isLoading={isLoading}
          pathname={location.pathname}
        />
      )}
    </main>
  );
};
// ...

hoc/with_loading.js

// ...
const withLoading = (WrappedComponent) => ({isLoading, ...props}) => {
  return isLoading ?
  <div className="loading-block">
    <FontAwesomeIcon icon={faSpinner} size="2x" color="#777" pulse />
  </div> :
  <WrappedComponent {...props} />;
};

export default withLoading;

之後就只剩render NewsList和NewsListItem要處理了!覺得畫面UI處理因人而異,就不另述了~
明天會再詳述各則新聞內容顯示處理。只能說,一開始以為能收到完整新聞資料的我真的是太蠢了!

今天處理的部份操作畫面如下:
https://i.imgur.com/J5n0r94.gif
明天再繼續寫內頁部分!


上一篇
Day 22-使用Query Parameters提供todos的過濾條件吧
下一篇
Day 24-透過URL Parameters處理News內頁
系列文
React 30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言