Skip to content

Commit

Permalink
Added search and list components with implemented redux
Browse files Browse the repository at this point in the history
  • Loading branch information
mczeplowski committed Jul 29, 2019
1 parent ad9a688 commit 179491c
Show file tree
Hide file tree
Showing 16 changed files with 218 additions and 4 deletions.
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@
"@babel/runtime": "^7.5.5",
"axios": "^0.19.0",
"clean-webpack-plugin": "^3.0.0",
"debounce": "^1.2.0",
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-lazyload": "^2.6.2",
"react-redux": "^7.1.0",
"redux": "^4.0.4",
"redux-thunk": "^2.3.0",
"styled-components": "^4.3.2",
"webpack": "^4.38.0",
"webpack-cli": "^3.3.6",
Expand Down
2 changes: 2 additions & 0 deletions src/components/AppComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import React, { Fragment } from 'react';
import { Container } from './AppStyles';
import Header from './header/HeaderComponent';
import Search from './search/SearchComponent';
import List from './list/ListComponent';

const AppComponent = () => (
<Fragment>
<Header />
<Container>
<Search />
<List />
</Container>
</Fragment>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/header/HeaderComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { Header, Logo } from './HeaderStyles';
const HeaderComponent = () => (
<Header>
<Logo>
<span>Search</span>
<span>Gif</span>
<span>Search</span>
</Logo>
</Header>
);
Expand Down
1 change: 1 addition & 0 deletions src/components/header/HeaderStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ export const Logo = styled.header`
span:first-of-type {
color: #5294e2;
text-transform: uppercase;
}
`;
37 changes: 37 additions & 0 deletions src/components/list/ListComponent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Item from './item/ItemComponent';

const ListComponent = ({ data, isLoading }) => {
if (isLoading) {
return <h1>Loading..</h1>;
}

return (
<div>
{!!data.length &&
data.map((img, i) => {
const key = `image-${i}`;
return <Item key={key} url={img.url} height={img.height} />;
})}
</div>
);
};

const mapStateToProps = state => ({
isLoading: state.list.isLoading,
data: state.list.data,
});

ListComponent.propTypes = {
isLoading: PropTypes.bool.isRequired,
data: PropTypes.arrayOf(
PropTypes.shape({
url: PropTypes.string,
height: PropTypes.number,
}),
).isRequired,
};

export default connect(mapStateToProps)(ListComponent);
19 changes: 19 additions & 0 deletions src/components/list/item/ItemComponent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import LazyLoad from 'react-lazyload';
import { ItemContainer } from './ItemStyles';

const ItemComponent = ({ url, height }) => (
<ItemContainer>
<LazyLoad height={height}>
<img src={url} />
</LazyLoad>
</ItemContainer>
);

ItemComponent.propTypes = {
url: PropTypes.string.isRequired,
height: PropTypes.number.isRequired,
};

export default ItemComponent;
9 changes: 9 additions & 0 deletions src/components/list/item/ItemStyles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import styled from 'styled-components';

export const ItemContainer = styled.div`
display: flex;
justify-content: center;
margin: 50px auto;
padding: 20px;
background: #5e636f;
`;
39 changes: 37 additions & 2 deletions src/components/search/SearchComponent.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
import React from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { debounce } from 'debounce';
import { Input } from './SearchStyles';
import { fetch as fetchList } from '../../redux/actions/list';

const Search = () => <div>test</div>;
const SearchComponent = props => {
const fetch = debounce(phrase => {
props.fetchList(phrase);
}, 1100);

export default Search;
const onChange = e => {
const phrase = e.target.value;

if (phrase.length > 2) {
fetch(phrase);
}
};

return <Input placeholder="Enter phrase.." onChange={onChange} />;
};

const mapDispatchToProps = dispatch =>
bindActionCreators(
{
fetchList,
},
dispatch,
);

SearchComponent.propTypes = {
fetchList: PropTypes.func.isRequired,
};

export default connect(
null,
mapDispatchToProps,
)(SearchComponent);
11 changes: 11 additions & 0 deletions src/components/search/SearchStyles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import styled from 'styled-components';

export const Input = styled.input`
width: 100%;
background: #5e636f;
border: none;
border-bottom: 4px solid #5294e2;
font-size: 24px;
color: #fff;
padding: 10px;
`;
1 change: 1 addition & 0 deletions src/index.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>GIFSearch</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,700&display=swap" rel="stylesheet">
Expand Down
14 changes: 13 additions & 1 deletion src/index.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
import React from 'react';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import { render } from 'react-dom';
import thunk from 'redux-thunk';

import App from './components/AppComponent';
import reducers from './redux/reducers';

const store = createStore(reducers, applyMiddleware(thunk));

render(<App />, document.getElementById('app'));
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app'),
);
30 changes: 30 additions & 0 deletions src/redux/actions/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import axios from 'axios';
import { FETCH_FAIL, FETCH_REQUESTED, FETCH_SUCCESS } from '../constants/list';

const fetchFail = () => ({
type: FETCH_FAIL,
});

const fetchRequested = () => ({
type: FETCH_REQUESTED,
});

const fetchSuccess = payload => ({
type: FETCH_SUCCESS,
payload,
});

export const fetch = phrase => dispatch => {
dispatch(fetchRequested());

axios
.get(`http://localhost:8081/search?phrase=${phrase}`)
.then(response => {
if (response.error) {
return dispatch(fetchFail());
}

return dispatch(fetchSuccess(response.data.data));
})
.catch(() => dispatch(fetchFail()));
};
3 changes: 3 additions & 0 deletions src/redux/constants/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const FETCH_REQUESTED = 'FETCH_REQUESTED';
export const FETCH_SUCCESS = 'FETCH_SUCCESS';
export const FETCH_FAIL = 'FETCH_FAIL';
6 changes: 6 additions & 0 deletions src/redux/reducers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { combineReducers } from 'redux';
import list from './list';

export default combineReducers({
list,
});
29 changes: 29 additions & 0 deletions src/redux/reducers/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { FETCH_FAIL, FETCH_REQUESTED, FETCH_SUCCESS } from '../constants/list';

const initState = {
isLoading: false,
data: [],
};

export default (state = initState, action) => {
switch (action.type) {
case FETCH_REQUESTED:
return {
...state,
isLoading: true,
};
case FETCH_SUCCESS:
return {
...state,
isLoading: false,
data: action.payload,
};
case FETCH_FAIL:
return {
...state,
isLoading: false,
};
default:
return state;
}
};

0 comments on commit 179491c

Please sign in to comment.