├── .gitignore
├── .babelrc
├── index.js
├── constants
└── ActionTypes.js
├── server.js
├── webpack.config.js
├── components
├── ShopSearch.js
├── DuckCart.js
├── DucksToBuy.js
└── Duck.js
├── webpack.config.min.js
├── containers
├── App.js
└── DucksApp.js
├── actions
└── CartActions.js
├── data
└── getData.js
├── reducers
└── cart.js
├── README.md
├── index.html
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["transform-object-rest-spread", "jsx-display-if"],
3 | "presets": ["es2015", "react"]
4 | }
5 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './containers/App';
4 |
5 | ReactDOM.render(
6 | ,
7 | document.getElementById('root')
8 | );
9 |
--------------------------------------------------------------------------------
/constants/ActionTypes.js:
--------------------------------------------------------------------------------
1 | export const ADD_TO_CART = 'ADD_TO_CART';
2 | export const REMOVE_FROM_CART = 'REMOVE_FROM_CART';
3 | export const SET_DUCKS = 'SET_DUCKS';
4 | export const SET_LOADING = 'SET_LOADING';
5 | export const SET_QUERY = 'SET_QUERY';
6 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var WebpackDevServer = require('webpack-dev-server');
3 | var config = require('./webpack.config');
4 |
5 | new WebpackDevServer(webpack(config), {
6 | publicPath: config.output.publicPath,
7 | hot: true,
8 | historyApiFallback: true,
9 | stats: {
10 | colors: true
11 | }
12 | }).listen(3000, 'localhost', function (err) {
13 | if (err) {
14 | console.log(err);
15 | }
16 |
17 | console.log('Listening at localhost:3000');
18 | });
19 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | './index'
8 | ],
9 | output: {
10 | path: path.join(__dirname, 'dist'),
11 | filename: 'bundle.js',
12 | publicPath: '/dist/'
13 | },
14 | module: {
15 | rules: [
16 | {
17 | loader: 'babel-loader',
18 | resource: {
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | }
22 | }
23 | ]
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/components/ShopSearch.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import * as CartActions from '../actions/CartActions';
4 |
5 | function ShopSearch({query, onChange, onSubmit}) {
6 |
7 | return (
8 |
`
26 |
27 |
28 | ## Run Example
29 |
30 | `npm install`
31 |
32 | `npm start`
33 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Shop Ducks
4 |
8 |
9 |
10 |
15 |
16 |
17 |
18 |
19 |
27 |
28 |
--------------------------------------------------------------------------------
/components/Duck.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | export const addButtonStyles = {
5 | 'margin': '5px',
6 | 'padding': '5px',
7 | 'backgroundColor': 'lightgray',
8 | 'border': '2px solid gray',
9 | 'cursor': 'pointer',
10 | };
11 |
12 | export default function Duck(props) {
13 |
14 | let {duck, inCart, addToCart} = props;
15 | let duckStyles = {
16 | 'border': 'solid gray 2px',
17 | 'textAlign': 'center',
18 | 'margin': '3px',
19 | 'padding': '3px',
20 | };
21 | let duckTitleStyle = {
22 | 'maxWidth': '240px',
23 | 'textOverflow': 'ellipsis',
24 | 'whiteSpace': 'nowrap',
25 | 'overflow': 'hidden',
26 | };
27 | return (
28 |
29 |

30 |
31 |
{duck.title || duck.date_taken}
32 |
35 |
36 |
37 | )
38 | }
39 |
40 | Duck.propTypes = {
41 | duck: PropTypes.shape({
42 | date_taken: PropTypes.string,
43 | media: PropTypes.shape({
44 | m: PropTypes.string,
45 | }),
46 | title: PropTypes.string,
47 | }),
48 | addToCart: PropTypes.func,
49 | inCart: PropTypes.bool,
50 | }
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-with-redux-shop-ducks",
3 | "version": "1.0.0",
4 | "description": "shopping cart example with react and redux",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node server.js",
8 | "build": "webpack",
9 | "build-min": "NODE_ENV=production webpack --config webpack.config.min.js",
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/hartzis/react-with-redux-shop-ducks.git"
15 | },
16 | "author": "",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/hartzis/react-with-redux-shop-ducks/issues"
20 | },
21 | "homepage": "https://github.com/hartzis/react-with-redux-shop-ducks#readme",
22 | "dependencies": {
23 | "immutable": "^3.7.4",
24 | "prop-types": "^15.6.1",
25 | "react": "^16.3.0",
26 | "react-dom": "^16.3.0",
27 | "react-redux": "^5.0.7",
28 | "redux": "^3.7.2",
29 | "redux-thunk": "^1.0.0"
30 | },
31 | "devDependencies": {
32 | "babel-core": "^6.23.0",
33 | "babel-loader": "^6.2.0",
34 | "babel-plugin-transform-object-rest-spread": "^6.3.13",
35 | "babel-preset-es2015": "^6.3.13",
36 | "babel-preset-react": "^6.3.13",
37 | "babel-plugin-jsx-display-if": "^3.0.0",
38 | "node-libs-browser": "^0.5.2",
39 | "react-hot-loader": "^1.2.8",
40 | "webpack": "^2.2.0",
41 | "webpack-dev-server": "^2.3.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/containers/DucksApp.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { bindActionCreators } from 'redux';
3 | import { connect } from 'react-redux';
4 | import DucksToBuy from '../components/DucksToBuy';
5 | import DuckCart from '../components/DuckCart';
6 | import ShopSearch from '../components/ShopSearch';
7 | import * as CartActions from '../actions/CartActions';
8 |
9 | class DucksApp extends Component {
10 |
11 | constructor(props, context) {
12 | super(props, context);
13 | this._onSubmit = this._onSubmit.bind(this);
14 | }
15 |
16 | componentDidMount() {
17 | this.props.getDucks();
18 | }
19 |
20 | _onSubmit(e) {
21 | e && e.preventDefault && e.preventDefault();
22 | this.props.getDucks();
23 | }
24 |
25 | render() {
26 | const { ducks, ducksInCart, addToCart, removeFromCart, loading } = this.props;
27 | return (
28 |
29 |
Shop ducks with redux!
30 | * some ducks might not actually be ducks
31 |
32 |
33 |
36 |
37 |
38 |
43 |
44 | );
45 | }
46 | }
47 |
48 | function mapStateToProps(state) {
49 | return {
50 | ducks: state.cart.get('ducks').toJS(),
51 | ducksInCart: state.cart.get('ducksInCart').toJS(),
52 | loading: state.cart.get('loading'),
53 | }
54 | }
55 |
56 | export default connect(mapStateToProps, CartActions)(DucksApp);
57 |
--------------------------------------------------------------------------------