Last active
August 20, 2017 16:30
-
-
Save nonlogos/5e91fdf65352a68d1a2c90d9f9967bfc to your computer and use it in GitHub Desktop.
New React and React Router 4
This file contains hidden or 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
//actions | |
const SET_SEARCH_TERM = 'SET_SEARCH_TERM'; | |
export function setSearchTerm(searchTerm) { | |
return { | |
type: SET_SEARCH_TERM, | |
payload: seartchTerm | |
}; | |
} | |
//reducers | |
const setSearchTerm = (state, action) => Object.assign({}, state, { searchTerm: action.payload }) | |
//root reducer | |
import {SET_SEARCH_TERM} from ... | |
const DEFAULT_STATE = { | |
searchTerm: '' | |
} | |
const rootReducer = (state = DEFAULT_STATE, action) => { | |
switch (action.type) { | |
case SET_SEARCH_TERM: | |
return setSearchTerm(state, action); | |
default: | |
return state; | |
} | |
}; | |
export default rootReducer; |
This file contains hidden or 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
class Details extends Component { | |
constructor(props) { | |
super(props); | |
state = { | |
apiData: { rating: '' } | |
}; | |
} | |
componentDidMount() { | |
axios | |
.get(`http://localhost:3000/${this.props.show.imdgID}`) | |
.then(response => this.setState({apiData: response.data})); | |
} | |
props: { | |
show: Show | |
}; | |
render() { | |
const { title, description, year, poster, trailer } = this.props.show; | |
let ratingComponent; | |
if (this.state.apiData.rating) { | |
ratingComponent = <h3>{this.state.apiData.rating}</h3>; | |
} else { | |
ratingComponent = <Spinner />; | |
} | |
return ( | |
<div className="details"> | |
<header> | |
<h1>svideo</h1> | |
<section> | |
<h1>{title}</h1> | |
<h2>({Year})</h2> | |
{ratingComponent} | |
<img src={`/public/img/posters/${poster}`} alt=`poster for ${title}` /> | |
<p>{description}</p> | |
</section> | |
<div> | |
<iframe | |
src={`blah blah`} | |
frameBorder="0" | |
allowFullScreen | |
title={`Trailer for ${title}`} | |
/> | |
</div> | |
</header> | |
</div> | |
); | |
} | |
} |
This file contains hidden or 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'; | |
const Details = (props) => ( | |
<div className="details"> | |
<pre><code>{JSON.stringify(props, null, 4)}</code></pre> | |
</div> | |
); |
This file contains hidden or 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 { connect } from 'react-redux'; | |
import { setSearchTerm } from './actionCreators'; | |
//if it was a class we can use decorator | |
@connect(mapStateToProps); | |
const Landing = (props) => ( | |
<div className="Landing"> | |
<input onChange={props.handleSearchTermChange} value={props.searchTerm} | |
type="text" placeholder="Search" /> | |
</div> | |
); | |
const mapStateToProps = (state) => ({ searchTerm: state.searchTerm }) | |
const mapDispatchToProps = (dispatch) => ({ | |
handleSearchTermChange(event) { | |
dispatch(setSearchTerm(event.target.value)); | |
} | |
}) | |
// if using decorator | |
export default Landing | |
export default connect(mapStateToProps, mapDispatchToProps)(Landing); |
This file contains hidden or 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'; | |
const details = props => { | |
// destructuring | |
const { title, description, year, poster, trailer } = props.show; | |
return ( | |
<div className="details"> | |
<header> | |
<h1>svideo</h1> | |
<section> | |
<h1>{title}</h1> | |
<h2>({Year})</h2> | |
<img src={`/public/img/posters/${poster}`} alt=`poster for ${title}` /> | |
<p>{description}</p> | |
</section> | |
<div> | |
<iframe | |
src={`blah blah`} | |
frameBorder="0" | |
allowFullScreen | |
title={`Trailer for ${title}`} | |
/> | |
</div> | |
</header> | |
</div> | |
); | |
} |
This file contains hidden or 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
// types.js | |
export Show = { | |
title: string, | |
description: string, | |
year: string, | |
imdbID: string, | |
trailer: string, | |
poster: string | |
} | |
// regular component | |
import show from '../types.js' | |
props: { | |
shows: Array<Show> | |
} |
This file contains hidden or 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 { shallow } from 'enzyme'; | |
import preload from '../../data.json'; | |
import ShowCard from '../ShowCard'; | |
test('search should render correct amount of shows', () => { | |
const component = shallow(<Search />); | |
expect(component.find(ShowCard.length)).toEqual(preload.shows.length)); | |
}); | |
// xtest or xit or xdescribe will stop it from running | |
test('Search sould render correct amount of shows based on search term', () => { | |
const searchWord = 'black'; | |
const component = shallow(<Search />); | |
component.find('input').simulate('change', {target: {value: searchWord}}); | |
const showCount = preload.shows.filter( | |
show => `${show.title} ${show.description}`.toUpperCase().indexOf(searchWord.toUpperCase()) >= 0 | |
).length; | |
expect(component.find(ShowCard).length).toEqual(showCount); | |
}); | |
// or use describe to create a test suite | |
describe('Search', () => { | |
it('renders correctly', () => { | |
const component = shallow(<Search />); | |
expect(component).toMatchSnapshot(); | |
}); | |
it('should render correct amount of shows', () => { | |
const component = shallow(<Search />); | |
expect(component.find(ShowCard.length)).toEqual(preload.shows.length)); | |
}); | |
}); |
This file contains hidden or 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 ShowCard from './showCard'; | |
import preload from '../data.json'; | |
const Search = () => ( | |
{preload.shows.map(show => <showCard key={show.id} {...show} />)} // with spread operator | |
); | |
export default Search; | |
// child nested component | |
import React from 'react'; | |
import { string } from 'prop-types'; | |
const ShowCard = props => ( | |
<div className ='show-card'> | |
<img alt={`${props.title} Show Poster`} src={`/public/img/posters/${props.poster}`} /> | |
<div> | |
<h3>{props.title}</h3> | |
<h4>({props.year})</h4> | |
<p>{props.description}</p> | |
</div> | |
</div> | |
); | |
ShowCard.propTypes ={ | |
poster: string.isRequired, | |
title: string.isRequired, | |
year: string.isRequired, | |
description: string.isRequired, | |
}; | |
export default ShowCard | |
// map and filter | |
<div> | |
{preload.shows | |
.filter( | |
show => | |
`${show.title} ${show.description}`.toUpperCase().indexOf(this.state.searchTerm.toUpperCase()) >= 0 | |
) | |
.map(show => <ShowCard key={show.imbdID} {...show} />)} | |
</div> |
This file contains hidden or 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
// ownProps is the props that's passed in from parent props | |
import { getAPIData } from './actionCreators'; | |
componentDidMount() { | |
if (!this.props.rating) { | |
this.props.getAPIData(); | |
} | |
} | |
const mapStateToProps = (state, ownProps) => { | |
const apiData = state.apiData[ownProps.show.imdbID] ? state.apiData[ownProps.show.imdbId] | |
: {}; | |
return { | |
rating: apiData.rating | |
} | |
} | |
const mapDispatchToProps = (dispatch, ownProps) => ({ | |
getAPIData() { | |
dispatch(getAPIData(ownProps.show.imdbID)); | |
} | |
}); | |
export default connect(mapStateToProps, mapDispatchToProps); |
This file contains hidden or 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
//reducers | |
export const SET_SEARCH_TERM = 'SET_SEARCH_TERM'; | |
export const ADD_API_DATA = 'ADD_API_DATA'; | |
import { combineReducers } from 'redux'; | |
import { SET_SEARCH_TERM, ADD_API_DATA } from './actions'; | |
const searchTerm = (state = '', action) => { | |
if (action.type === SET_SEARCH_TERM) return action.payload; | |
return state; | |
}; | |
const apiData = (state = {}, action) => { | |
if (action.type === ADD_API_DATA){ | |
returnn Object.assign({}, state, { [action.payload.imdbID]: action.payload }) | |
} | |
return state; | |
} | |
const rootReducer = combineReducers({ searchTerm, apiData }); | |
export default rootReducer; | |
// action creators | |
import axios from 'axios'; | |
import { ADD_API_DATA } from './actions'; | |
export function addAPIData(apiData) { | |
return {type: ADD_API_DATA, payload: apiData}; | |
}; | |
// thunk that returns a function/deferred action | |
export function getAPIDetails(imdbID) { | |
return (dispatch) => { | |
axios | |
.get(`http://localhost:3000/${imdbID}`) | |
.then(response => { | |
// dispatch the action back to redux | |
dispatch(addAPIData(response.data)) | |
}) | |
.catch(error => { | |
console.error('axios error', error); //eslint-disable-line no-console | |
}); | |
}; | |
} |
This file contains hidden or 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 { BrowserRouter, Route, Switch } from 'react-router-dom'; | |
import Landing from './Landing'; | |
import Search from './Search'; | |
import Details from './Details'; | |
const FourOhFour = () => <h1>404</h1> | |
const App = () => ( | |
<BrowserRouter> | |
<div className="app"> | |
<Switch> | |
<Route exact path="/" component={Landing} /> | |
<Route path="/search" component={Search} /> | |
<Route path="details/:id" component={props => <Details show={preload.shows.find(show => props.match.params.id === show.imdbID)} {...props} />} /> | |
<Route component={FourOhFour} /> | |
</Switch> | |
</div> | |
</BrowserRouter> | |
); |
This file contains hidden or 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
// spinner.jsx | |
import React from 'react'; | |
import sytled, { keyframs } from 'styled-components'; | |
const spin = keyframes` | |
from { | |
transform: rotate(0deg); | |
} to { | |
transform: rotate(360deg); | |
} | |
`; | |
const Image = styled.img` | |
animation: ${spin} 4s infinite linear; | |
`; | |
const Spinner = () => <Image src="/public/img/loading.png" alt="loading indicator" /> | |
export default Spinner; |
This file contains hidden or 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
//inside of constructor | |
this.state = { | |
searchTerm: '' | |
}; | |
this.handleSearchTermChange = this.handleSearchTermChange.bind(this); | |
// JSX | |
<input onChange={this.handleSearchTermChange} value={this.state.searchTerm} type="text" placeholder="search" /> | |
// class methods | |
handleSearchTermChange(event) { | |
this.setState({searchTerm: event.target.value}); | |
} | |
// with the babel class property - don't need constructor | |
this.state = { | |
searchTerm: '' | |
}; | |
handleSearchTermChange = event => { | |
this.setState({searchTerm: event.target.value}); | |
} | |
This file contains hidden or 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 styled from 'styled-components'; | |
const Wrapper = styled.div` | |
width: 32%; | |
border: 2px solid #333; | |
border-radius: 4px; | |
margin-bottom: 25px; | |
padding-right: 10px; | |
overflow: hidden; | |
`; | |
const Image = Styles.img` | |
width: 46%; | |
float: left; | |
margin-right: 10px; | |
`; | |
<Wrapper> | |
<image alt={`${props.title} Show Poster`} src={`/public/img/posters/${props.poster}`} /> | |
<div> | |
<h3>{props.title}</h3> | |
<h4>({props.year})</h4> | |
<p>{props.description}</p> | |
</div> | |
</Wrapper> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment