Created
December 15, 2018 16:48
-
-
Save Dmitriy-8-Kireev/98d46e232a425cffa5cab17e6d5e62b5 to your computer and use it in GitHub Desktop.
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
// INDEX.JS | |
import React from 'react'; | |
import ReactDOM from 'react-dom'; | |
import './index.css'; | |
import AppRouter from 'components/AppRouter'; | |
import getStore from './store'; | |
import { Provider } from 'react-redux'; | |
import { BrowserRouter } from 'react-router-dom'; | |
const store = getStore(); | |
ReactDOM.render( | |
<BrowserRouter basename="/search-serials"> | |
<Provider store={store}> | |
<AppRouter /> | |
</Provider> | |
</BrowserRouter>, | |
document.getElementById('root') | |
); | |
// | |
//API.JS | |
// | |
xport const search = query => | |
fetch(`http://api.tvmaze.com/search/shows?q=${query}`, { | |
method: 'GET', | |
mode: 'cors', | |
}) | |
.then(response => response.json()) | |
.then(shows => shows.map(show => show.show)); | |
export const show = showId => | |
fetch(`http://api.tvmaze.com/shows/${showId}?embed=cast`, { | |
method: 'GET', | |
mode: 'cors', | |
}).then(response => response.json()); | |
// | |
//STORE.JS | |
// | |
import { createStore, applyMiddleware, compose } from 'redux'; | |
import rootReducer from './reducers'; | |
import showMiddleware from './middlewares/showMiddleware'; | |
import searchMiddleware from './middlewares/searchMiddleware'; | |
export default initialState => | |
createStore( | |
rootReducer, | |
initialState, | |
compose( | |
applyMiddleware(searchMiddleware, showMiddleware), | |
window.devToolsExtension ? window.__REDUX_DEVTOOLS_EXTENSION__() : f => f, | |
), | |
); | |
//SRC | |
//ACTIONS | |
//searchActions.js | |
import { createActions } from 'redux-actions'; | |
export const { searchRequest, searchSuccess, searchFailure } = createActions({ | |
SEARCH_REQUEST: undefined, | |
SEARCH_SUCCESS: undefined, | |
SEARCH_FAILURE: undefined | |
}); | |
//SRC | |
//ACTIONS | |
//showActions.js | |
import { createActions } from 'redux-actions'; | |
export const { showRequest, showSuccess, showFailure } = createActions( | |
'SHOW_REQUEST', | |
'SHOW_SUCCESS', | |
'SHOW_FAILURE' | |
); | |
//SRC | |
//MIDDLEWARES | |
//searchMiddleware.js | |
import { search } from '../api'; | |
import { | |
searchRequest, | |
searchSuccess, | |
searchFailure | |
} from '../actions/searchActions'; | |
export default store => next => action => { | |
if (action.type === searchRequest.toString()) { | |
const query = action.payload; | |
search(query) | |
.then(series => { | |
store.dispatch(searchSuccess(series)); | |
}) | |
.catch(e => { | |
store.dispatch(searchFailure(e.toString())); | |
}); | |
} | |
return next(action); | |
}; | |
//SRC | |
//MIDDLEWARES | |
//showMiddleware.js | |
import { showRequest, showSuccess, showFailure } from '../actions/showActions'; | |
import { show } from '../api'; | |
export default store => next => action => { | |
if (action.type === showRequest.toString()) { | |
show(action.payload) | |
.then(entities => { | |
store.dispatch(showSuccess(entities)); | |
}) | |
.catch(error => { | |
store.dispatch(showFailure(error)); | |
}); | |
} | |
return next(action); | |
}; | |
//SRC | |
//REDUCERS | |
//index.js | |
import { combineReducers } from 'redux'; | |
import search from './search'; | |
import shows from './shows'; | |
export default combineReducers({ | |
search, | |
shows, | |
}); | |
//SRC | |
//REDUCERS | |
//search.js | |
import { | |
searchRequest, | |
searchSuccess, | |
searchFailure | |
} from '../actions/searchActions'; | |
import { combineReducers } from 'redux'; | |
import { handleAction } from 'redux-actions'; | |
const films = handleAction( | |
searchSuccess, | |
(state, action) => action.payload, | |
[] | |
); | |
const error = handleAction( | |
searchFailure, | |
(state, action) => action.error, | |
null | |
); | |
const isFetching = (state = false, action: payloadAction): boolean => { | |
switch (action.type) { | |
case searchRequest: | |
return true; | |
case searchSuccess: | |
return false; | |
case searchFailure: | |
return false; | |
default: | |
return state; | |
} | |
}; | |
const isFetched = (state = false, action: payloadAction): boolean => { | |
switch (action.type) { | |
case searchRequest: | |
return false; | |
case searchSuccess: | |
return true; | |
case searchFailure: | |
return true; | |
default: | |
return state; | |
} | |
}; | |
export default combineReducers({ | |
error, | |
films, | |
isFetched, | |
isFetching | |
}); | |
export const getfilms = state => state.films; | |
export const getIsFetching = state => state.isFetching; | |
export const getIsFetched = state => state.isFetched; | |
export const getError = state => state.error; | |
//SRC | |
//REDUCERS | |
//shows.js | |
import { showRequest, showSuccess, showFailure } from '../actions/showActions'; | |
import { combineReducers } from 'redux'; | |
import { handleAction } from 'redux-actions'; | |
const show = handleAction(showSuccess, (state, action) => action.payload, []); | |
const error = handleAction(showFailure, (state, action) => action.error, null); | |
const isFetching = (state = false, action: payloadAction): boolean => { | |
switch (action.type) { | |
case showRequest: | |
return true; | |
case showSuccess: | |
return false; | |
case showFailure: | |
return false; | |
default: | |
return state; | |
} | |
}; | |
const isFetched = (state = false, action: payloadAction): boolean => { | |
switch (action.type) { | |
case showRequest: | |
return false; | |
case showSuccess: | |
return true; | |
case showFailure: | |
return true; | |
default: | |
return state; | |
} | |
}; | |
export default combineReducers({ | |
error, | |
show, | |
isFetched, | |
isFetching | |
}); | |
export const getShow = state => state.show; | |
export const getIsFetching = state => state.isFetching; | |
export const getIsFetched = state => state.isFetched; | |
export const getError = state => state.error; | |
//SRC | |
//COMPONENTS | |
//AppRouter.js используется вместе с index.js export { default } from './AppRouter'; | |
import React, { Component } from 'react'; | |
import './AppRouter.css'; | |
import { Route, Switch, withRouter } from 'react-router-dom'; | |
import Search from 'components/Search'; | |
import ShowPage from 'components/ShowPage'; | |
class AppRouter extends Component { | |
render() { | |
return ( | |
<div className="App"> | |
<Switch> | |
<Route exact path="/" component={Search} /> | |
<Route path="/shows/:id" component={ShowPage} /> | |
</Switch> | |
</div> | |
); | |
} | |
} | |
export default withRouter(AppRouter); | |
//SRC | |
//COMPONENTS | |
//Search.js используется вместе с index.js export { default } from './Search'; | |
import React, { Component } from 'react'; | |
import { connect } from 'react-redux'; | |
import { Link } from 'react-router-dom'; | |
import { searchRequest } from '../../actions/searchActions'; | |
import ShowPreview from '../ShowPreview/ShowPreview'; | |
class Search extends Component { | |
constructor(props) { | |
super(props); | |
this.state = { searchInput: '' }; | |
} | |
hanleInputChange = e => { | |
const { name, value } = e.target; | |
this.setState({ [name]: value }); | |
}; | |
handleSearch = () => { | |
this.props.searchRequest(this.state.searchInput); | |
}; | |
render() { | |
const { isFetching, films } = this.props; | |
const { searchInput } = this.state; | |
return ( | |
<div className="Search"> | |
<div className="SearchForm"> | |
<input | |
type="text" | |
placeholder="Название сериала" | |
name="searchInput" | |
value={searchInput} | |
onChange={this.hanleInputChange} | |
/> | |
<button onClick={this.handleSearch}>Найти</button> | |
</div> | |
<div className="SearсhResult"> | |
{isFetching ? ( | |
<div> | |
<p>Загрузка...</p> | |
</div> | |
) : ( | |
<ul> | |
{films.map(film => ( | |
<article className="results-item" key={film.id}> | |
<h3> | |
<Link to={`/shows/${film.id}`}>{film.name}</Link> | |
</h3> | |
{films.map(({ id, name, image, summary }) => ( | |
<ShowPreview | |
key={id} | |
id={id} | |
name={name} | |
image={image} | |
summary={summary} | |
/> | |
))} | |
</article> | |
))} | |
</ul> | |
)} | |
</div> | |
</div> | |
); | |
} | |
} | |
const mapStateToProps = state => ({ | |
isFetching: state.search.isFetching, | |
films: state.search.films | |
}); | |
const mapDispatchToProps = { | |
searchRequest | |
}; | |
export default connect( | |
mapStateToProps, | |
mapDispatchToProps | |
)(Search); | |
//SRC | |
//COMPONENTS | |
//ShowPage.js используется вместе с index.js export { default } from './ShowPage' | |
import React, { Component } from 'react'; | |
import { connect } from 'react-redux'; | |
import { showRequest } from '../../actions/showActions'; | |
export class ShowPage extends Component { | |
constructor(props) { | |
super(props); | |
this.props.showRequest(this.props.match.params.id); | |
} | |
render() { | |
const { isFetching, show } = this.props; | |
return ( | |
<div className="ShowPage"> | |
{isFetching ? ( | |
<div> | |
<p>Загрузка...</p> | |
</div> | |
) : ( | |
<div> | |
<div className="ShowPage_title" key={show.id}> | |
{show.name} | |
<br /> | |
{show.image && <img src={show.image.medium} alt={show.name} />} | |
<br /> | |
{show._embedded && | |
show._embedded.cast && | |
show._embedded.cast.map(this.Person)} | |
</div> | |
</div> | |
)} | |
</div> | |
); | |
} | |
Person = ({ person }) => ( | |
<div className="ShowPage_persons" key={person.id}> | |
{person.name} | |
<br /> | |
{person.image && <img src={person.image.medium} alt={person.name} />} | |
</div> | |
); | |
} | |
const mapStateToProps = state => ({ | |
isFetching: state.shows.isFetching, | |
show: state.shows.show | |
}); | |
const mapDispatchToProps = { | |
showRequest | |
}; | |
export default connect( | |
mapStateToProps, | |
mapDispatchToProps | |
)(ShowPage); | |
//SRC | |
//COMPONENTS | |
//ShowPreview.js используется вместе с index.js export { default } from './ShowPreview'; | |
import React, { PureComponent } from 'react'; | |
import { Link } from 'react-router-dom'; | |
export default class ShowPreview extends PureComponent { | |
render() { | |
const { id, name, image, summary } = this.props; | |
return ( | |
<li className="serial" key={id}> | |
<Link className="serial__link" to={`/shows/${id}`}> | |
{name} | |
</Link> | |
{image && <img className="serial__img" src={image.medium} alt={name} />} | |
<div | |
className="serial__summary" | |
dangerouslySetInnerHTML={{ __html: summary }} | |
/> | |
</li> | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment