Last active
October 27, 2018 14:44
-
-
Save eiriklv/52af4cb05b4eb386b4f24f50a748fa34 to your computer and use it in GitHub Desktop.
Idiomatic React and Redux
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 { getSearchResults } from './services'; | |
export const SEARCH_PENDING = 'SEARCH_PENDING'; | |
export const SEARCH_SUCCEEDED = 'SEARCH_SUCCEEDED'; | |
export const SEARCH_FAILED = 'SEARCH_FAILED'; | |
export function searchPending() { | |
return { | |
type: SEARCH_PENDING | |
}; | |
} | |
export function searchSucceeded(results) { | |
return { | |
type: SEARCH_SUCCEEDED, | |
results, | |
}; | |
} | |
export function searchFailed(error) { | |
return { | |
type: SEARCH_FAILED, | |
error, | |
}; | |
} | |
export async function performSearch(keyword) { | |
return async function(dispatch, getState) { | |
dispatch(searchPending()); | |
try { | |
const results = await getSearchResults(keyword); | |
dispatch(searchSucceeded(results)); | |
} catch (error) { | |
dispatch(searchFailed(error)); | |
} | |
} | |
} |
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
.App { | |
text-align: center; | |
} | |
.App-logo { | |
animation: App-logo-spin infinite 20s linear; | |
height: 10vmin; | |
} | |
.App-header { | |
background-color: #282c34; | |
min-height: 20vh; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
font-size: calc(10px + 2vmin); | |
color: white; | |
} | |
.App-link { | |
color: #61dafb; | |
} | |
@keyframes App-logo-spin { | |
from { | |
transform: rotate(0deg); | |
} | |
to { | |
transform: rotate(360deg); | |
} | |
} |
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 './App.css'; | |
import logo from './logo.svg'; | |
import { connect } from 'react-redux'; | |
import { bindActionCreators } from 'redux'; | |
import { performSearch } from './actions'; | |
class App extends Component { | |
constructor(props) { | |
super(props); | |
this.searchInput = React.createRef(); | |
} | |
handleSearch(event) { | |
const { performSearch } = this.props; | |
if (event.keyCode !== 13) { | |
return; | |
} | |
const keyword = this.searchInput.current.value; | |
performSearch(keyword); | |
} | |
render() { | |
const { | |
results, | |
isLoading, | |
error, | |
} = this.props; | |
return ( | |
<div className="App"> | |
<header className="App-header"> | |
<img src={logo} className="App-logo" alt="logo" /> | |
<p>Search Demo Application</p> | |
</header> | |
<div className="App-content-top"> | |
Book search: | |
<input | |
ref={this.searchInput} | |
type="text" | |
onKeyDown={this.handleSearch.bind(this)} | |
/> | |
</div> | |
<div className="App-content"> | |
<h3>Results:</h3> | |
{error && ( | |
<p>Failed: {error.message}</p> | |
)} | |
{isLoading && ( | |
<p>Loading..</p> | |
)} | |
{results && ( | |
<ul> | |
{results.map((entry) => { | |
return ( | |
<li key={entry.id}>Title: {entry.title}</li> | |
); | |
})} | |
</ul> | |
)} | |
</div> | |
</div> | |
); | |
} | |
} | |
function mapStateToProps(state) { | |
return { | |
isLoading: state.isLoading, | |
error: state.error, | |
results: state.results, | |
}; | |
} | |
function mapDispatchToProps(dispatch) { | |
return bindActionCreators({ | |
performSearch | |
}, dispatch); | |
} | |
export default connect( | |
mapStateToProps, | |
mapDispatchToProps | |
)(App); |
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 from 'react'; | |
import ReactDOM from 'react-dom'; | |
import './index.css'; | |
import App from './App'; | |
import { Provider } from 'react-redux'; | |
import configureStore from './store'; | |
const store = configureStore(); | |
ReactDOM.render( | |
<Provider store={store}> | |
<App /> | |
</Provider>, | |
document.getElementById('root') | |
); |
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 { | |
SEARCH_PENDING, | |
SEARCH_SUCCEEDED, | |
SEARCH_FAILED, | |
} from './actions'; | |
const initialState = { | |
results: [], | |
isLoading: false, | |
error: null, | |
}; | |
export default function rootReducer(state = initialState, action) { | |
switch (action.type) { | |
case SEARCH_PENDING: | |
return { | |
...state, | |
results: [], | |
isLoading: true, | |
error: null, | |
}; | |
case SEARCH_SUCCEEDED: | |
return { | |
...state, | |
results: action.results, | |
isLoading: false, | |
error: null, | |
}; | |
case SEARCH_FAILED: | |
return { | |
...state, | |
isLoading: false, | |
error: action.error, | |
}; | |
default: | |
return state; | |
} | |
} |
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
/** | |
* Run this file with `node server.js` | |
*/ | |
const express = require('express') | |
const app = express() | |
const cors = require('cors'); | |
const port = 5000; | |
function delay(ms) { | |
return new Promise((resolve) => { | |
setTimeout(() => { | |
resolve(); | |
}, ms); | |
}); | |
} | |
const data = [ | |
{ id: 1, title: 'SICP', description: 'Structure and Interpretation of Computer Programs is a textbook aiming to teach the principles of computer programming, such as abstraction in programming, metalinguistic abstraction, recursion, interpreters, and modular programming.' }, | |
{ id: 2, title: 'JavaScript The Good Parts', description: 'Most programming languages contain good and bad parts, but JavaScript has more than its share of the bad, having been developed and released in a hurry before it could be refined.' }, | |
]; | |
app.use(cors()); | |
app.get('/search', async (req, res) => { | |
await delay(3000); | |
res.send(data); | |
}); | |
app.listen(port, () => console.log(`API listening on port ${port}!`)) |
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 axios from 'axios'; | |
const api = axios.create({ | |
baseURL: 'http://localhost:5000', | |
timeout: 10000, | |
}); | |
/** | |
* Search service | |
*/ | |
export async function getSearchResults(keyword) { | |
const response = await api.get('/search', { | |
params: { keyword }, | |
}); | |
return response.data; | |
} |
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
function delay(ms) { | |
return new Promise((resolve) => { | |
setTimeout(() => { | |
resolve(); | |
}, ms); | |
}); | |
} | |
/** | |
* Fake service example | |
* (waits 3 seconds before returning results) | |
*/ | |
export async function getSearchResults(keyword) { | |
await delay(3000); | |
return [ | |
{ id: 1, title: 'SICP', description: 'Structure and Interpretation of Computer Programs is a textbook aiming to teach the principles of computer programming, such as abstraction in programming, metalinguistic abstraction, recursion, interpreters, and modular programming.' }, | |
{ id: 2, title: 'JavaScript The Good Parts', description: 'Most programming languages contain good and bad parts, but JavaScript has more than its share of the bad, having been developed and released in a hurry before it could be refined.' }, | |
]; | |
} |
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 { applyMiddleware, createStore } from 'redux'; | |
import thunkMiddleware from 'redux-thunk'; | |
import { composeWithDevTools } from 'redux-devtools-extension'; | |
import rootReducer from './reducers'; | |
export default function configureStore(preloadedState) { | |
const middlewares = [thunkMiddleware] | |
const middlewareEnhancer = applyMiddleware(...middlewares) | |
const enhancers = [middlewareEnhancer] | |
const composedEnhancers = composeWithDevTools(...enhancers) | |
const store = createStore(rootReducer, preloadedState, composedEnhancers) | |
return store; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment