Học React/Redux qua ví dụ thực tế: Kết nối với SoundCloud
Hôm nay sẽ là một sự trở lại nhẹ nhàng của Series Học React/Redux qua ví dụ thực tế. Chúng ta sẽ tìm cách kết nối application đang viết với SoundCloud API. Cùng bắt đầu nhé! Còn chờ gì nữa mà không đăng kí account SoundCloud! Nói vậy thôi chứ ai có account rồi thì khỏi đăng kí ...
Hôm nay sẽ là một sự trở lại nhẹ nhàng của Series Học React/Redux qua ví dụ thực tế. Chúng ta sẽ tìm cách kết nối application đang viết với SoundCloud API.
Cùng bắt đầu nhé!
Còn chờ gì nữa mà không đăng kí account SoundCloud!
Nói vậy thôi chứ ai có account rồi thì khỏi đăng kí nữa nhé. Hehe.
Sau khi đã có account, các bạn vào trang SoundCloud Developers, chọn SoundCloud API để đăng kí app mới. Hay click luôn vào link này https://soundcloud.com/you/apps/new để đăng kí nè.
Hiện nay đăng kí app mới trên SoundCloud Developers khá phức tạp nhưng mà các bạn hãy cố lên, mình tin tưởng ở các bạn.
Sau khi đăng kí app xong, các bạn nhớ set REDIRECT URI về local để có thể dev cho lẹ nhé.
Tuỳ theo môi trường của các bạn mà setting cho đúng nhé!
Chia sẻ thêm, trong nhiều trường hợp, ở đây mình nói với những API khác, ví dụ như facebook, phần Redirect URI sẽ không chấp nhận localhost, lúc này các bạn có thể sử dụng phương pháp tunnel server của các bạn về localhost để dev, hoặc dùng một số ứng dụng như ngrok để tạo tunnel dynamic nếu như bạn không có server. Chi tiết các bạn search thêm google nhé.
Quay trở lại bài học.
Tóm lại lúc này các bạn sẽ có trong tay CLIENT ID và REDIRECT URI của SoundCloud app.
Tiếp tục code nè!
Đầu tiên tạo file config để chứa mấy thứ này đã.
Tại src folder, tạo file config.js.
1 2 3 4 |
export const CLIENT_ID = process.env.CLIENT_ID; export const REDIRECT_URI = process.env.REDIRECT_URI || 'http://localhost:8080/callback'; |
Trong đó CLIENT_ID và REDIRECT_URI sẽ là environment variable.
Tại sao lại làm như vậy?
Lý do thứ nhất là vì security, chúng ta không thể khơi khơi mà ném những thông tin như vậy vào trong source code được, một lúc nào đó chúng ta open source ra, người ta thấy được thì phải làm sao, đâu phải ai cũng đạo đức như mình, thử nhìn qua thằng bạn Tôi đi code dạo của tui mà xem, nó phá nát bấy hầy bao nhiêu cái web rồi kìa, tốt nhất là đừng để những thông tin nhạy cảm leak vào trong source code.
Lý do thứ hai, đó là những thông tin này sẽ thay đổi tuỳ môi trường, các bạn không thể cứ đem lên staging/production là lao vào sửa code với thông tin tương ứng với staging/production được, phải để một chỗ nào chúng ta có thể linh động sửa được, chỗ đó là environment variable đó. Thế nhé, nhớ mà áp dụng vào thực tế nữa nhé!
Khi run app các bạn đơn giản nhập lệnh sau.
1 2 3 |
CLIENT_ID=your-client-id npm start |
Vì chúng ta set default cho REDIRECT_URL là http://localhost:8080/callback nên ở môi trường local chúng ta cũng chẳng cần specify làm gì.
Tiếp theo, install SoundCloud client.
1 2 3 |
npm --save install soundcloud |
Đây là library của SoundCloud đưa ra cho chúng ta xài. Có thì xài thôi, tội vạ gì phải implement mấy cái này cho vất vả đúng không nào.
Ding! Cài xong rồi chứ gì, mở file src/index.js lên code tiếp nè, chúng ta sẽ phải initialize SoundCloud client trước khi sử dụng.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
... import SoundCloud from 'soundcloud'; ... import {CLIENT_ID, REDIRECT_URI} from './config'; SoundCloud.initialize({ client_id: CLIENT_ID, redirect_uri: REDIRECT_URI }) ... const store = configureStore(); store.dispatch(actions.setTracks(tracks)); ReactDOM.render( <Provider store={store}> <TrackList /> </Provider>, document.getElementById('app') ); |
Ủa rồi làm sao xử lý cái /callback trên app bây giờ?
React router
Để login thông qua SoundCloud, chúng ta phải phụ thuộc vào route /callback. Vì thế chúng ta sẽ phải setup routing cho app, mà với react thì chúng ta đã có React Router hỗ trợ việc này, nào cùng setup react-router cùng với một số route đơn giản.
1 2 3 |
npm --save install react-router react-router-redux |
Sau khi cài đặt xong, các bạn mở file webpack.config.js thêm historyApiFallback: true vào config của devServer.
Config này nhằm cho phép application của chúng ta xử lý HTML5 router dễ dàng hơn, mà không cần care browser có support hay không.
Nào cùng tiếp tục phân tích. Bây giờ application của chúng ta sẽ có hai route, một để hiển thị app, chúng ta sẽ sử dụng root path /, và một để làm callback handle việc authentication thông qua SoundCloud API, chúng ta sẽ sử dụng /callback. Ngoài ra, chúng ta sẽ sử dụng react-router-redux để sync browser history và store. Điều này sẽ có ích khi route thay đổi. Có ích vì sao, hồi sau sẽ rõ.
Đầu tiên tạo file src/routes.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import React from 'react'; import {Route, IndexRoute} from 'react-router'; import App from './components/App'; import TrackList from './components/TrackList'; import Callback from './components/Callback'; export default function createRoutes() { return ( <Route path='/' component={App}> <IndexRoute component={TrackList} /> <Route path="/" component={TrackList} /> <Route path="/callback" component={Callback} /> </Route> ) } |
Trong file này chúng ta sẽ implement function createRoutes để khởi tạo routes cho app. Với component App sẽ là wrapper, và component tương ứng với route như mình đã nói ở trên.
Lại mở file src/index.js lên.
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 |
import React from 'react'; import ReactDOM from 'react-dom'; import {Provider} from 'react-redux'; import SoundCloud from 'soundcloud'; import {Router, browserHistory} from 'react-router' import {syncHistoryWithStore} from 'react-router-redux' import {configureStore} from './store'; import {CLIENT_ID, REDIRECT_URI} from './config'; import createRoutes from './routes'; SoundCloud.initialize({ client_id: CLIENT_ID, redirect_uri: REDIRECT_URI }) const store = configureStore(); const history = syncHistoryWithStore(browserHistory, store); const routes = createRoutes(); ReactDOM.render( <Provider store={store}> <Router history={history} routes={routes} /> </Provider>, document.getElementById('app') ); |
Lúc này chúng ta không cần render component TrackList nữa, bây giờ chúng ta cần render Router để init các route tương ứng mà chúng ta đã tạo với createRoutes. Đối với Router chúng ta còn cần thêm props history
Sẵn tiện tôi remove luôn mấy đoạn code thừa, chúng ta không cần stuff data nữa, sắp có data thật mà xài rồi, ah hihi.
Các bạn đã thấy có hai components mà chúng ta chưa implement, bây giờ sẽ bắt đầu implement từng cái một.
Tạo các folders và files trước, làm sao ra như thế này là ổn. Đừng hỏi mình tạo như thế nào nữa nhé, mình khóc thật đấy!
Đầu tiên là component App. Mở file App.js.
1 2 3 4 5 6 7 8 9 10 11 12 |
import React, {Component} from 'react'; export default class App extends Component { render() { return ( <div>{this.props.children}</div> ) } } |
Đơn giản quá phải không, App chỉ có nhiệm vụ render tất cả các children của nó, hiện giờ chúng ta chỉ cần thế này là đủ, nhưng trong tương lai, chúng ta có thể ném nhiều thứ hơn vào đây, ví dụ như Header, Footer, hay Player, etc..
Tiếp theo là Callback, mở file Callback.js.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import React, {Component} from 'react'; const TIME_OUT = 100 export default class Callback extends Component { componentDidMount() { setTimeout(opener.SoundCloud.connectCallback, TIME_OUT); } render() { return ( <div> <p>This page will close soon.</p> </div> ) } } |
Các bạn có câu hỏi gì không? Nếu có thì comment nhé, component này chỉ làm nhiệm vụ callback thôi, vì thế khi load xong chúng ta sẽ gọi hàm của SoundCloud client để execute callback, chúng ta sẽ chẳng phải đụng vào nó sau này nữa đâu.
Cuối cùng, để hoàn tất flow với react-router chúng ta sẽ phải cung cấp cho store cái state về routing để nó có thể chuyển page.
Mở src/reducers/index.js.
1 2 3 4 5 6 7 8 9 10 |
import {combineReducers} from 'redux'; import {routerReducer} from 'react-router-redux'; import tracks from './tracks'; export default combineReducers({ tracks, routing: routerReducer }); |
Mở src/store.js.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import {createStore, applyMiddleware} from 'redux'; import createLogger from 'redux-logger'; import {browserHistory} from 'react-router'; import {routerMiddleware} from 'react-router-redux'; import rootReducer from './reducers/index'; const logger = createLogger(); const router = routerMiddleware(browserHistory); const createStoreWithMiddleware = applyMiddleware(router, logger)(createStore); export function configureStore(initialState) { return createStoreWithMiddleware(rootReducer, initialState); } |
Chúng ta apply thêm middleware router vào store. Khi sync store với browser history, chúng ta có thể listen trên những event của current route. Trong series này có thể sẽ không đề cập đến những trường hợp đó, tuy nhiên các bạn có thể tưởng tượng như việc fetch data khi route thay đổi. Ngoài ra những thuộc tính như browser path hay query params trên URL cũng có thể access được từ store sau khi chúng ta làm chuyện này.
Thử start app nào, đừng quên environment variable CLIENT_ID nhé!
Lúc này trang trắng bóc, đừng vội hoảng sợ, mở console lên, nếu các bạn thấy action của react-router được dispatch thì các bạn đã thành công rồi đấy.
Trở lại với các bạn nhẹ nhàng thế này thôi, hôm sau chúng ta sẽ tiếp tục implement việc login vào SoundCloud để lấy data của user mà hiển thị nhé!
Source code trong bài các bạn vẫn có thể xem ở https://github.com/codeaholicguy/react-redux-tutorial/tree/master/connect-with-soundcloud
Techtalk via codealohicguy