Last active
April 16, 2017 06:10
-
-
Save saitoxu/705e41267e50dee76d28eb98849edd90 to your computer and use it in GitHub Desktop.
2017-04-16
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const REQUEST_REPOS = 'REQUEST_REPOS' | |
export const RECEIVE_REPOS = 'RECEIVE_REPOS' | |
export function fetchReposIfNeeded(username) { | |
return (dispatch, getState) => { | |
if (shouldFetchRepos(getState(), username)) { | |
return dispatch(fetchRepos(username)) | |
} | |
} | |
} | |
function fetchRepos(username) { | |
return (dispatch) => { | |
dispatch(requestRepos(username)) | |
return fetch(`https://api.github.com/users/${username}/repos`) | |
.then((response) => { | |
return response.json() | |
}) | |
.then((json) => { | |
if (json.message) { | |
throw Error(json.message) | |
} | |
return dispatch(receiveRepos(username, json)) | |
}) | |
.catch((err) => { | |
return dispatch(receiveRepos(username, [])) | |
}) | |
} | |
} | |
function requestRepos(username) { | |
return { | |
type: REQUEST_REPOS, | |
username | |
} | |
} | |
function receiveRepos(username, json) { | |
return { | |
type: RECEIVE_REPOS, | |
username, | |
repos: json.map(child => { | |
return { name: child.name } | |
}) | |
} | |
} | |
function shouldFetchRepos(state, username = '') { | |
const currentUsername = state.username | |
const isFetching = state.isFetching | |
if (username.length === 0 || currentUsername === username || isFetching) { | |
return false | |
} | |
return true | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react' | |
import { connect } from 'react-redux' | |
import { fetchReposIfNeeded } from '../actions' | |
import Form from '../components/Form' | |
import Repos from '../components/Repos' | |
class AsyncApp extends Component { | |
constructor(props) { | |
super(props) | |
this.handleSubmit = this.handleSubmit.bind(this) | |
} | |
handleSubmit(nextUser) { | |
this.props.dispatch(fetchReposIfNeeded(nextUser)) | |
} | |
render() { | |
const { username, isFetching, repos } = this.props | |
return ( | |
<div> | |
<Form value={username} onSubmit={this.handleSubmit} /> | |
{isFetching && repos.length === 0 && | |
<h2>Loading...</h2> | |
} | |
{!isFetching && repos.length === 0 && | |
<h2>Empty.</h2> | |
} | |
{repos.length > 0 && | |
<div style={{ opacity: isFetching ? 0.5 : 1 }}> | |
<Repos repos={repos} /> | |
</div> | |
} | |
</div> | |
) | |
} | |
} | |
function mapStateToProps(state) { | |
const { reposByUser } = state | |
const { username, isFetching, repos } = reposByUser | |
return { | |
username, | |
isFetching, | |
repos | |
} | |
} | |
export default connect(mapStateToProps)(AsyncApp) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createStore, applyMiddleware } from 'redux' | |
import thunkMiddleware from 'redux-thunk' | |
import rootReducer from './reducers' | |
export default function configureStore(preloadedState) { | |
return createStore( | |
rootReducer, | |
preloadedState, | |
applyMiddleware( | |
thunkMiddleware | |
) | |
) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react' | |
export default class Form extends Component { | |
render() { | |
const { value, onSubmit } = this.props | |
let input | |
return ( | |
<div> | |
<form onSubmit={e => { | |
e.preventDefault() | |
if (!input.value.trim()) { | |
return | |
} | |
onSubmit(input.value) | |
}}> | |
<input ref={node => { | |
input = node | |
}} /> | |
<button type="submit"> | |
Fetch | |
</button> | |
</form> | |
<h2>{value}</h2> | |
</div> | |
) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { combineReducers } from 'redux' | |
import { | |
REQUEST_REPOS, | |
RECEIVE_REPOS | |
} from './actions' | |
function reposByUser(state = { username: '', isFetching: false, repos: [] }, action) { | |
switch (action.type) { | |
case REQUEST_REPOS: | |
return Object.assign({}, state, { | |
username: action.username, | |
isFetching: true | |
}) | |
case RECEIVE_REPOS: | |
return Object.assign({}, state, { | |
isFetching: false, | |
repos: action.repos | |
}) | |
default: | |
return state | |
} | |
} | |
const rootReducer = combineReducers({ | |
reposByUser | |
}) | |
export default rootReducer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react' | |
export default class Repos extends Component { | |
render() { | |
return ( | |
<ul> | |
{this.props.repos.map((repo, i) => | |
<li key={i}>{repo.name}</li> | |
)} | |
</ul> | |
) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react' | |
import { Provider } from 'react-redux' | |
import configureStore from '../configureStore' | |
import AsyncApp from './AsyncApp' | |
const store = configureStore() | |
export default class Root extends Component { | |
render() { | |
return ( | |
<Provider store={store}> | |
<AsyncApp /> | |
</Provider> | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment