質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

0回答

537閲覧

SPAでAPIの読み込みやデータのありなし処理をもっとスマートに書きたい

kumakuma112

総合スコア21

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

1グッド

0クリップ

投稿2020/01/11 14:03

編集2020/01/11 14:04

タイトルで、SPAと書きましたが、
今作ってるものはポータルサイトで開発の都合で、
動的ページはフレームワークのHyperappを使い、静的ページはhtmlとcssで通常のコーディングをしてます。
工数削減という理由もあり、ページ間の移動はSPAではなくリンクで遷移してます。

お聞きしたい課題

待機時間改善のためにFacebookのようなデータがないときのスケルトンスクリーンの実装をしてます。
その結果、色々改善しないといけない点が出てしまい、苦戦してます。

  1. api読み込みの書き方もっと良い方法はないか?
  2. 複数のAPIのデータを使い何かactionを実行したい場合、データありの場合でif文書くと無限ループに陥る
  3. スケルトンスクリーンのもっとスマートな書き方がないか
  4. リロード時に少しでもスクロール中だと高確率でページの最上部に位置が変わってしまう

イメージ説明

データ説明

  • Hyperapp
  • axios

API

  • GET / master(最初に読み込む設定データ)
  • GET / top(トップページのデータ)
  • GET / news(ニュースのデータ)

構成

sample/ ├─ src/ │   ├─ stores/ │   │  └─ modules/ │   │  ├─ Store.js │   ├─ views/ │   │  └─ component/ │   │  └─ layout/ │   │  └─ project/ │   │  └─ Page/ │   │     └─ Top.js │   │  └─ App.js │   ├─ index.js ├─ www/ │   ├─ index.html │   ├─ css/ │   ├─ js/ │   ├─ images/ ├─ package.json ├─ webpack.config.js

src/index.js

jacascript

1import { app } from 'hyperapp' 2import { State, Actions } from 'src/stores/Store.js' 3import App from 'src/views/App.js' 4import route from 'src/route.js'; 5 6let routeInfo = route(); // urlから {type,params..} を取得 7app(State, Actions, App, document.body).init(routeInfo);

src/Store/store.js

javascript

1import axios from 'axios'; 2import StoreTop from 'src/stores/modules/top/StoreTop.js'; 3import StoreNews from 'src/stores/modules/news/StoreNews.js'; 4 5export default { 6 state: { 7 route: null, 8 master: null, 9 top: StoreTop.state, 10 news: StoreNews.state, 11 }, 12 actions: { 13 top: StoreTop.actions, 14 news: StoreNews.actions, 15 // 初期設定 16 init: routeInfo => (state, actions) => { 17 actions.getMaterData(); // 共通データを先に取得 18 if (routeInfo) { 19 // url情報をrouteに反映 20 return { 21 route: routeInfo 22 }; 23 } 24 }, 25 //-------------------------------------------------- 26 // 【API】共通 27 //-------------------------------------------------- 28 // ローカルストレージに保存する処理も必要だがまだ実装しきれてない 29 getData: ({ key, apiPath, params, fn }) => (state, actions) => { 30 let url = null; 31 // ローカルのJSONデータ読み込みの名残でゲットパラメータをテキストでつなげてます 32 let paramsText = null; 33 if (params) { 34 for (let key in params) { 35 if (params[key]) { 36 paramsArray.push(key + '=' + encodeURIComponent(params[key])); 37 } 38 } 39 if (paramsArray.length > 0) { 40 paramsText = '?' + paramsArray.join('&'); 41 } 42 } 43 if (paramsText) { 44 url = path + apiPath + paramsText; 45 } else { 46 url = path + apiPath; 47 } 48 49 //actions.loading.open(); 50 actions.loadApi({ 51 key: key, 52 url: url, 53 fn: result => { 54 actions.getResult({ key: key, result: result, fn: fn }); 55 } 56 }); 57 }, 58 loadApi: ({ key, url, fn }) => (state, actions) => { 59 axios.get(url).then(res => { 60 if (!res.error) { 61 const data = res.data; 62 fn && 63 fn({ 64 success: true, 65 message: `API-${key} Success`, 66 data: data 67 }); 68 } else { 69 fn && 70 fn({ 71 success: false, 72 error: res.error, 73 message: res.errorMessage, 74 data: null 75 }); 76 } 77 }); 78 }, 79 getResult: ({ key, result, fn }) => (state, actions) => { 80 if (result.success) { 81 switch (key) { 82 case 'master': 83 actions.setMasterData(result.data); 84 break; 85 case 'top': 86 actions.top.setTopData(result.data); 87 break; 88 case 'news': 89 actions.news.setNewsData(result.data); 90 break; 91 } 92 //actions.loading.close(); 93 fn && fn(); 94 } else { 95 //actions.loading.close(); 96 console.log(result.message); 97 } 98 }, 99 //-------------------------------------------------- 100 // 【API】master 101 //-------------------------------------------------- 102 getMaterData: ({ fn }) => (state, actions) => { 103 actions.getData({ 104 key: 'master', 105 apiPath: 'https://xxxx.com/api/master', 106 params: null, 107 fn: () => { 108 fn && fn(); 109 } 110 }); 111 }, 112 setMasterData: data => (state, actions) => { 113 return { 114 master: data 115 }; 116 }, 117 //-------------------------------------------------- 118 // 【API】トップ 119 //-------------------------------------------------- 120 getTopData: ({ fn }) => (state, actions) => { 121 actions.getData({ 122 key: 'top', 123 apiPath: 'https://xxxx.com/api/top', 124 params: null, 125 fn: () => { 126 fn && fn(); 127 } 128 }); 129 }, 130 //-------------------------------------------------- 131 // 【API】ニュース 132 //-------------------------------------------------- 133 getNewsData: ({ year, category, limit, fn }) => (state, actions) => { 134 actions.getData({ 135 key: 'news', 136 apiPath: 'https://xxxx.com/api/news', 137 params: { 138 year: year, 139 category: category, 140 limit: limit 141 }, 142 fn: () => { 143 fn && fn(); 144 } 145 }); 146 }, 147 } 148};

src/App.js

javascript

1import { h } from 'hyperapp'; 2import Top from 'src/views/page/Top.js'; 3export default (state, actions) => { 4 console.log('State : ', state); 5 return ( 6 <div> 7 <Top /> 8 </div> 9 ); 10};

src/Page/Top.js

javascript

1import { h } from 'hyperapp'; 2import styles from 'src/views/page/top/top.scss'; 3export default (props, children) => (state, actions) => { 4 if (state.route.type === 'top') { 5 // この書き方で同じAPIを複数回実行してしまわないか不安 6 if (state.master && state.top.data == null) { 7 actions.getTopData({ 8 fn: () => { 9 actions.getNewsData({ 10 year: null, 11 category: null, 12 limit: 10, 13 fn: null 14 }); 15 } 16 }); 17 } 18 if (state.master && state.top.data) { 19 // masterとtopデータを使いたい場合、この書き方だとstate更新の無限ループ陥る 20 actions.top.setYear(state.master.year|| state.top.data.year); 21 } 22 return ( 23 <div key="top" class={styles['top']}> 24 <PTopMainvisual 25 data={state.top.data && state.top.data.mainvisualItems[0]} 26 dummyData={state.top.dummyData.mainvisualItems} 27 isLoading={state.top.data ? 0 : 1} 28 /> 29 // nullにすればAPIから来たときの空データ[]と別物扱いしますか? 30 {state.news.data == null ? ( 31 <div> 32 {state.news.data.length > 0 && ( 33 <PTopSection title="ニュース"> 34 <PList type="news" data={state.news.data} isLoading={0} /> 35 </PTopSection> 36 )} 37 </div> 38 ) : ( 39 <PTopSection title="ニュース"> 40 <PList type="news" dummyData={state.news.dummyData} isLoading={1} /> 41 </PTopSection> 42 )} 43 </div> 44 ); 45 } 46};

src/project/list.js

javascript

1import { h } from 'hyperapp'; 2export default (props, children) => (state, actions) => { 3 return ( 4 <div class={[styles['p-list'], props.class].join(' ')}> 5 {props.isLoading == 1 ? ( 6 // APIからデータ受け取ってなかったら 7 <div> 8 {[...Array(props.dummyData.limit)].map(item => { 9 return ( 10 <image src="/images/dummy/text.png"> 11 ); 12 })} 13 </div> 14 ) : ( 15 // APIからデータ受け取っていたら 16 <div> 17 {props.data && ( 18 // データが有れば 19 <div> 20 {props.data.map(item => { 21 return ( 22 <div>{item.text}</div> 23 ); 24 })} 25 </div> 26 )} 27 </div> 28 )} 29 </div> 30 ); 31};

1. api読み込みの書き方もっと良い方法はないか?

待機時間の向上のため、データがなくてもダミーで表示できるようにしてます。
そのため、APIが揃う前にpageをreturnで表示させてます。
if (state.master && state.top.data == null) {...}で書いてる箇所は、問題ないか?

2. 複数のAPIのデータを使い何かactionを実行したい場合、データありの場合でif文書くと無限ループに陥る

1回だけ実行してほしいのに無限に実行してしまい、処理に負担がかかる。
またAPIから返ってきたデータがエラーではなく、1件もなしのようなケースも想定できていないので、改善したいが、
どうしたら良いのか?

javascript

1if (state.master && state.top.data) { 2 actions.top.setYear(state.master.year|| state.top.data.year); 3}

3. スケルトンスクリーンのもっとスマートな書き方がないか

同じコードを何回も書いてる気がして、もっと良い方法がないのか。
src/page/Top.js
src/project/List.js

{state.news.data ? ( // APIデータ来た場合 <div> // APIデータが1件でもあったら表示 {state.news.data.length > 0 && ( <PTopSection title="ニュース"> <PList type="news" data={state.news.data} isLoading={0} /> </PTopSection> )} </div> ) : ( // APIデータ来てないときの状態 <PTopSection title="ニュース"> <PList type="news" dummyData={state.news.dummyData} isLoading={1} /> </PTopSection> )}

またstate.news.data ?この書き方で、空配列はどちらの判定なるのか?
nullは、true
[]はfalseだとダミーデータが表示されるので困る

4. リロード時に少しでもスクロール中だと高確率でページの最上部に位置が変わってしまう

これは、原因と対策の検討もついてないです。

退会済みユーザー👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問