File tree 13 files changed +400
-38
lines changed
13 files changed +400
-38
lines changed Original file line number Diff line number Diff line change
1
+ {
2
+ "presets" : [
3
+ " env" ,
4
+ " next/babel"
5
+ ],
6
+ "plugins" : [
7
+ [" module-resolver" , {
8
+ "root" : [" ./src" ]
9
+ }]
10
+ ]
11
+ }
Original file line number Diff line number Diff line change 1
1
{
2
2
"dependencies" : {
3
+ "axios" : " ^0.17.1" ,
4
+ "babel-plugin-module-resolver" : " ^3.0.0" ,
5
+ "babel-preset-env" : " ^1.6.1" ,
3
6
"compression" : " ^1.7.1" ,
4
7
"express" : " ^4.16.2" ,
5
- "next" : " ^4.1.2" ,
8
+ "humps" : " ^2.0.1" ,
9
+ "immutable" : " ^3.8.2" ,
10
+ "next" : " 4.2.0" ,
11
+ "next-redux-wrapper" : " ^1.3.5" ,
6
12
"next-routes" : " ^1.1.0" ,
7
- "react" : " ^16.0.0" ,
8
- "react-dom" : " ^16.0.0"
13
+ "prop-type" : " ^0.0.1" ,
14
+ "react" : " 16.2.0" ,
15
+ "react-dom" : " 16.2.0" ,
16
+ "react-redux" : " ^5.0.6" ,
17
+ "redux" : " ^3.7.2" ,
18
+ "redux-logger" : " ^3.0.6" ,
19
+ "redux-thunk" : " ^2.2.0"
9
20
},
10
21
"name" : " nextjs-real-world" ,
11
22
"version" : " 1.0.0" ,
Original file line number Diff line number Diff line change 1
- import { Link } from '../routes'
1
+ import withReduxStore from 'store/createStore'
2
+ import SearchRepoContainer from 'containers/SearchRepoContainer'
2
3
3
- export default ( ) => (
4
- < div className = 'hello' >
5
- next.js real world
6
- < div >
7
- < Link route = '/about' > About</ Link >
8
- </ div >
9
- < style jsx > { `
10
- .hello {
11
- font: 20px Helvetica, Arial, sans-serif;
12
- background: #f8f8f8;
13
- color: #b0b0b0;
14
- padding: 2em;
15
- height: 100vh;
16
- text-align: center;
17
- }
18
- ` } </ style >
19
- </ div >
20
- )
4
+ export default withReduxStore ( SearchRepoContainer )
Original file line number Diff line number Diff line change 1
1
const nextRoutes = require ( 'next-routes' )
2
2
const routes = module . exports = nextRoutes ( )
3
3
4
- routes . add ( 'about' , '/about' )
4
+ const APP_ROUTES = [ {
5
+ page : 'index' ,
6
+ pattern : '/'
7
+ } , {
8
+ page : 'about' ,
9
+ pattern : '/about'
10
+ } ]
11
+
12
+ APP_ROUTES . forEach ( route => routes . add ( route ) )
Original file line number Diff line number Diff line change
1
+
2
+ import github from 'libs/github'
3
+
4
+ export const GET_TOP_REPOS = Symbol ( 'GET_TOP_REPOS' )
5
+ export const GET_TOP_REPOS_SUCCESS = Symbol ( 'GET_TOP_REPOS_SUCCESS' )
6
+
7
+ export function getTopRepos ( { lang } ) {
8
+ return dispatch => {
9
+
10
+ dispatch ( {
11
+ type : GET_TOP_REPOS
12
+ } )
13
+
14
+ return github . getTopRepos ( { lang } ) . then ( res => {
15
+ dispatch ( onGetTopRepo ( lang , res ) )
16
+ } )
17
+ }
18
+ }
19
+
20
+ function onGetTopRepo ( lang , payload ) {
21
+ return {
22
+ type : GET_TOP_REPOS_SUCCESS ,
23
+ lang,
24
+ payload
25
+ }
26
+ }
Original file line number Diff line number Diff line change
1
+ import React , { Fragment } from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ const REPO_COUNT = 10
5
+ const SearchResults = ( { repos } ) => {
6
+ return (
7
+ < Fragment >
8
+ < h2 > Top { REPO_COUNT } { repos . get ( 'lang' ) } repos</ h2 >
9
+ < small > { repos . get ( 'totalCount' ) . toLocaleString ( ) } found</ small >
10
+ < ul >
11
+ {
12
+ repos . get ( 'items' ) . take ( REPO_COUNT ) . map ( item => (
13
+ < li key = { item . get ( 'id' ) } >
14
+ < a href = { item . get ( 'url' ) } target = '_blank' >
15
+ { item . get ( 'name' ) }
16
+ </ a >
17
+ </ li >
18
+ ) )
19
+ }
20
+ </ ul >
21
+ </ Fragment >
22
+ )
23
+ }
24
+
25
+ SearchResults . propTypes = {
26
+ repos : PropTypes . instanceOf ( Map ) . isRequired ,
27
+ }
28
+
29
+ export default SearchResults
Original file line number Diff line number Diff line change
1
+ export default {
2
+ env : process . env . NODE_ENV ,
3
+ mode : process . env . MODE ,
4
+ githubApiEndpoint : process . env . GITHUB_API_ENDPOINT ,
5
+ }
Original file line number Diff line number Diff line change
1
+ import React , { Component } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { Map } from 'immutable'
4
+ import { connect } from 'react-redux'
5
+
6
+ import config from 'config'
7
+ import { getTopRepos } from 'actions/repos'
8
+ import SearchResults from 'components/SeachResults'
9
+
10
+ class SearchRepoContainer extends Component {
11
+
12
+ static async getInitialProps ( { store, query } ) {
13
+
14
+ let lang = query . lang || 'javascript'
15
+ await store . dispatch ( getTopRepos ( { lang } ) )
16
+ }
17
+
18
+ componentDidMount ( ) {
19
+ let { getTopRepos } = this . props
20
+ getTopRepos ( { lang : 'ruby' } )
21
+ }
22
+
23
+ render ( ) {
24
+ let { repos } = this . props
25
+ return (
26
+ < SearchResults repos = { repos } />
27
+ )
28
+ }
29
+
30
+ }
31
+
32
+ function mapStateToProps ( state ) {
33
+ return {
34
+ repos : state . repos
35
+ }
36
+ }
37
+
38
+ SearchRepoContainer . propTypes = {
39
+ repos : PropTypes . instanceOf ( Map ) . isRequired ,
40
+ getTopRepos : PropTypes . func . isRequired
41
+ }
42
+
43
+ export { SearchRepoContainer }
44
+ export default connect ( mapStateToProps , {
45
+ getTopRepos
46
+ } ) ( SearchRepoContainer )
Original file line number Diff line number Diff line change
1
+ import axios from 'axios'
2
+ import humps from 'humps'
3
+
4
+ const ENDPOINT = 'https://api.github.com'
5
+
6
+ const github = {
7
+ getTopRepos ( { lang = 'javascript' } ) {
8
+ let path = `${ ENDPOINT } /search/repositories?q=language:${ lang } &sort=stars&order=desc`
9
+ return axios . get ( path ) . then ( res => {
10
+ return humps . camelizeKeys ( res . data )
11
+ } )
12
+ }
13
+ }
14
+
15
+ export default github
Original file line number Diff line number Diff line change
1
+ import { combineReducers } from 'redux'
2
+
3
+ import repos from './repos'
4
+
5
+ export default combineReducers ( {
6
+ repos
7
+ } )
Original file line number Diff line number Diff line change
1
+ import Immutable from 'immutable'
2
+ import * as ActionType from 'actions/repos'
3
+
4
+ export const initialState = Immutable . fromJS ( {
5
+ isLoading : false ,
6
+ lang : ''
7
+ } )
8
+
9
+ export default function ( state = initialState , action ) {
10
+ switch ( action . type ) {
11
+
12
+ case ActionType . GET_TOP_REPOS :
13
+ return state . set ( 'isLoading' , true )
14
+
15
+ case ActionType . GET_TOP_REPOS_SUCCESS :
16
+ return state . merge (
17
+ Object . assign ( { } , action . payload , {
18
+ isLoading : false ,
19
+ lang : action . lang
20
+ } )
21
+ )
22
+
23
+ default :
24
+ return state
25
+ }
26
+ }
Original file line number Diff line number Diff line change
1
+ import Immutable from 'immutable'
2
+ import thunkMiddleware from 'redux-thunk'
3
+ import { createLogger } from 'redux-logger'
4
+ import withRedux from 'next-redux-wrapper'
5
+ import { createStore , applyMiddleware , compose } from 'redux'
6
+
7
+ import config from 'config'
8
+ import rootReducer from '../reducers'
9
+
10
+ function createMiddlewares ( { isServer } ) {
11
+
12
+ let middlewares = [
13
+ thunkMiddleware ,
14
+ ]
15
+
16
+ if ( config . env === 'development' && typeof window !== 'undefined' ) {
17
+ middlewares . push ( createLogger ( {
18
+ level : 'info' ,
19
+ collapsed : true ,
20
+ stateTransformer : ( state ) => {
21
+ let newState = { }
22
+
23
+ for ( let i of Object . keys ( state ) ) {
24
+ if ( Immutable . Iterable . isIterable ( state [ i ] ) ) {
25
+ newState [ i ] = state [ i ] . toJS ( )
26
+ } else {
27
+ newState [ i ] = state [ i ]
28
+ }
29
+ }
30
+
31
+ return newState
32
+ }
33
+ } ) )
34
+ }
35
+
36
+ return middlewares
37
+ }
38
+
39
+ function immutableChildren ( obj ) {
40
+ let state = { }
41
+ Object . keys ( obj ) . map ( ( key ) => {
42
+ state [ key ] = Immutable . fromJS ( obj [ key ] )
43
+ } )
44
+ return state
45
+ }
46
+
47
+ export const initStore = ( initialState = { } , context ) => {
48
+
49
+ let { isServer } = context
50
+ let middlewares = createMiddlewares ( { isServer } )
51
+ let state = immutableChildren ( initialState )
52
+
53
+ return createStore (
54
+ rootReducer ,
55
+ state ,
56
+ compose ( applyMiddleware ( ...middlewares ) )
57
+ )
58
+ }
59
+
60
+ export default ( comp ) => withRedux ( initStore ) ( comp )
You can’t perform that action at this time.
0 commit comments