27 | );
28 | }
29 | }
30 |
31 | // Get state data from store to props
32 | const mapStateToProps = (state) => {
33 | return {
34 | items: state.items,
35 | hasErrored: state.itemsHasErrored,
36 | isLoading: state.itemsIsLoading
37 | };
38 | }
39 |
40 | // Get actions to handle store data
41 | const mapDispatchToProps = (dispatch) => {
42 | return {
43 | fetchData: (url) => dispatch(itemsFetchData(url))
44 | };
45 | }
46 |
47 | // Wire it all up and export
48 | export default connect(mapStateToProps, mapDispatchToProps)(ItemList);
49 |
--------------------------------------------------------------------------------
/basic-redux-thunk/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Basic Redux Thunk
8 |
13 |
14 |
15 |
16 |
React Redux Thunk
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/basic-redux-thunk/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 |
4 | import { Provider } from 'react-redux';
5 | import configureStore from './store/configureStore';
6 |
7 | import ItemList from './components/ItemList';
8 |
9 | // Init store
10 | const store = configureStore();
11 |
12 | // Provider to use redux store in app
13 | render(
14 |
15 |
16 | ,
17 | document.getElementById('app')
18 | );
--------------------------------------------------------------------------------
/basic-redux-thunk/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { items, itemsHasErrored, itemsIsLoading } from './items';
3 |
4 | // Combine all reducers into root reducer
5 | export default combineReducers({
6 | items,
7 | itemsHasErrored,
8 | itemsIsLoading
9 | });
--------------------------------------------------------------------------------
/basic-redux-thunk/src/reducers/items.js:
--------------------------------------------------------------------------------
1 | // Reducers for error, loading and data fetch
2 |
3 | export const itemsHasErrored = (state = false, action) => {
4 | switch (action.type) {
5 | case 'ITEMS_HAS_ERRORED':
6 | return action.hasErrored;
7 |
8 | default:
9 | return state;
10 | }
11 | }
12 |
13 | export const itemsIsLoading = (state = false, action) => {
14 | switch (action.type) {
15 | case 'ITEMS_IS_LOADING':
16 | return action.isLoading;
17 |
18 | default:
19 | return state;
20 | }
21 | }
22 |
23 | export const items = (state = [], action) => {
24 | switch (action.type) {
25 | case 'ITEMS_FETCH_DATA_SUCCESS':
26 | return action.items;
27 |
28 | default:
29 | return state;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/basic-redux-thunk/src/store/configureStore.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import rootReducer from '../reducers';
4 |
5 | // Configure the store with reducers, initial state (if provided) and react-thunk
6 | export default function configureStore(initialState) {
7 | return createStore(
8 | rootReducer,
9 | initialState,
10 | applyMiddleware(thunk)
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/book-store/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "latest",
4 | "react",
5 | "stage-1"
6 | ],
7 | "env": {
8 | "development": {
9 | "presets": [
10 | "react-hmre"
11 | ]
12 | },
13 | "production": {
14 | "plugins": [
15 | "transform-react-constant-elements",
16 | "transform-react-remove-prop-types"
17 | ]
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/book-store/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/book-store/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": [
4 | "eslint:recommended",
5 | "plugin:import/errors",
6 | "plugin:import/warnings"
7 | ],
8 | "plugins": [
9 | "react"
10 | ],
11 | "parser": "babel-eslint",
12 | "parserOptions": {
13 | "ecmaVersion": 6,
14 | "sourceType": "module",
15 | "ecmaFeatures": {
16 | "jsx": true,
17 | "experimentalObjectRestSpread": true
18 | }
19 | },
20 | "env": {
21 | "es6": true,
22 | "browser": true,
23 | "node": true,
24 | "jquery": true,
25 | "mocha": true
26 | },
27 | "rules": {
28 | "quotes": 0,
29 | "no-console": 1,
30 | "no-debugger": 1,
31 | "no-var": 1,
32 | "semi": [1, "always"],
33 | "no-trailing-spaces": 0,
34 | "eol-last": 0,
35 | "no-underscore-dangle": 0,
36 | "no-alert": 0,
37 | "no-lone-blocks": 0,
38 | "jsx-quotes": 1,
39 | "react/display-name": [ 1, {"ignoreTranspilerName": false }],
40 | "react/forbid-prop-types": [1, {"forbid": ["any"]}],
41 | "react/jsx-boolean-value": 0,
42 | "react/jsx-closing-bracket-location": 0,
43 | "react/jsx-curly-spacing": 1,
44 | "react/jsx-indent-props": 0,
45 | "react/jsx-key": 1,
46 | "react/jsx-max-props-per-line": 0,
47 | "react/jsx-no-bind": 0,
48 | "react/jsx-no-duplicate-props": 1,
49 | "react/jsx-no-literals": 0,
50 | "react/jsx-no-undef": 1,
51 | "react/jsx-pascal-case": 1,
52 | "react/jsx-sort-prop-types": 0,
53 | "react/jsx-sort-props": 0,
54 | "react/jsx-uses-react": 1,
55 | "react/jsx-uses-vars": 1,
56 | "react/no-danger": 1,
57 | "react/no-did-mount-set-state": 1,
58 | "react/no-did-update-set-state": 1,
59 | "react/no-direct-mutation-state": 1,
60 | "react/no-multi-comp": 1,
61 | "react/no-set-state": 0,
62 | "react/no-unknown-property": 1,
63 | "react/prefer-es6-class": 1,
64 | "react/prop-types": 1,
65 | "react/react-in-jsx-scope": 1,
66 | "react/require-extension": 1,
67 | "react/self-closing-comp": 1,
68 | "react/sort-comp": 1,
69 | "react/wrap-multilines": 1
70 | },
71 | "globals": {
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/book-store/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Node version:
2 |
3 | npm version:
4 |
5 | Operating system:
6 |
7 | Command line used:
8 |
9 | Steps to reproduce:
10 |
--------------------------------------------------------------------------------
/book-store/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log*
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
29 | #dist folder
30 | dist
31 |
32 | # IDEA/Webstorm project files
33 | .idea
34 | *.iml
35 |
36 | #VSCode metadata
37 | .vscode
38 |
39 | # Mac files
40 | .DS_Store
41 |
--------------------------------------------------------------------------------
/book-store/.istanbul.yml:
--------------------------------------------------------------------------------
1 | instrumentation:
2 | excludes: ['*.spec.js']
3 | extensions: ['.js']
4 |
--------------------------------------------------------------------------------
/book-store/.npmrc:
--------------------------------------------------------------------------------
1 | save-exact=true
2 |
--------------------------------------------------------------------------------
/book-store/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | before_install:
3 | # Repo for Yarn
4 | - sudo apt-key adv --keyserver pgp.mit.edu --recv D101F7899D41F3C3
5 | - echo "deb http://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
6 | - sudo apt-get update -qq
7 | - sudo apt-get install -y -qq yarn
8 | install:
9 | - yarn install
10 | cache:
11 | directories:
12 | - $HOME/.yarn-cache
13 | node_js:
14 | - "6"
15 | - "5"
16 | - "4"
17 | after_success:
18 | # Send coverage data to coveralls.
19 | - npm run test:cover:travis
20 |
--------------------------------------------------------------------------------
/book-store/appveyor.yml:
--------------------------------------------------------------------------------
1 | # Test against this version of Node.js
2 | environment:
3 | matrix:
4 | # node.js
5 | - nodejs_version: "6"
6 | - nodejs_version: "5"
7 | - nodejs_version: "4"
8 |
9 | # Install scripts. (runs after repo cloning)
10 | install:
11 | # Get the latest stable version of Node.js or io.js
12 | - ps: Install-Product node $env:nodejs_version
13 | # install modules
14 | - yarn
15 |
16 | cache:
17 | - "%LOCALAPPDATA%/Yarn"
18 |
19 | # Post-install test scripts.
20 | test_script:
21 | # Output useful info for debugging.
22 | - node --version
23 | - npm --version
24 | # run tests
25 | - npm test
26 |
27 | # Don't actually build.
28 | build: off
29 |
30 | notifications:
31 | - provider: Email
32 | to:
33 | - housecor@gmail.com
34 | subject: 'Build failed: react-slingshot'
35 | message: The continuous integration build failed. See https://ci.appveyor.com/project/CoryHouse/react-slingshot/ for details.
36 | on_build_success: false
37 | on_build_failure: true
38 | on_build_status_changed: false
39 |
--------------------------------------------------------------------------------
/book-store/docs/FAQ.md:
--------------------------------------------------------------------------------
1 | ##FAQ
2 | ###Why does this exist?
3 | This starter kit implements best practices like testing, minification, bundling, and so on. It codifies a long list of decisions that you no longer have to make to get rolling. It saves you from the long, painful process of wiring it all together into an automated dev environment and build process. It's also useful as inspiration for ideas you might want to integrate into your current development environment or build process.
4 |
5 | ###What do the scripts in package.json do?
6 | Unfortunately, scripts in package.json can't be commented inline because the JSON spec doesn't support comments, so I'm providing info on what each script in package.json does here.
7 |
8 | | **Script** | **Description** |
9 | |----------|-------|
10 | | remove-demo | Removes the demo application so you can begin development. |
11 | | prestart | Runs automatically before start. Calls remove-dist script which deletes the dist folder. This helps remind you to run the build script before committing since the dist folder will be deleted if you don't. ;) |
12 | | start | Runs tests, lints, starts dev webserver, and opens the app in your default browser. |
13 | | lint:tools | Runs ESLint on build related JS files. (eslint-loader lints src files via webpack when `npm start` is run) |
14 | | clean-dist | Removes everything from the dist folder. |
15 | | remove-dist | Deletes the dist folder. |
16 | | create-dist | Creates the dist folder and the necessary subfolders. |
17 | | prebuild | Runs automatically before build script (due to naming convention). Cleans dist folder, builds html, and builds sass. |
18 | | build | Bundles all JavaScript using webpack and writes it to /dist. |
19 | | test | Runs tests (files ending in .spec.js) using Mocha and outputs results to the command line. Watches all files so tests are re-run upon save. |
20 | | test:cover | Runs tests as described above. Generates a HTML coverage report to ./coverage/index.html |
21 | | test:cover:travis | Runs coverage as described above, however sends machine readable lcov data to Coveralls. This should only be used from the travis build! |
22 |
23 | ### Can you explain the folder structure?
24 | ```
25 | .
26 | ├── .babelrc # Configures Babel
27 | ├── .editorconfig # Configures editor rules
28 | ├── .eslintrc # Configures ESLint
29 | ├── .gitignore # Tells git which files to ignore
30 | ├── .istanbul.yml # Configure istanbul code coverage
31 | ├── .npmrc # Configures npm to save exact by default
32 | ├── README.md # This file.
33 | ├── dist # Folder where the build script places the built app. Use this in prod.
34 | ├── package.json # Package configuration. The list of 3rd party libraries and utilities
35 | ├── src # Source code
36 | │ ├── actions # Flux/Redux actions. List of distinct actions that can occur in the app.
37 | │ ├── components # React components
38 | │ ├── constants # Application constants including constants for Redux
39 | │ ├── containers # Top-level React components that interact with Redux
40 | │ ├── favicon.ico # favicon to keep your browser from throwing a 404 during dev. Not actually used in prod build.
41 | │ ├── index.ejs # Template for homepage
42 | │ ├── index.js # Entry point for your app
43 | │ ├── reducers # Redux reducers. Your state is altered here based on actions
44 | │ ├── store # Redux store configuration
45 | │ ├── styles # CSS Styles, typically written in Sass
46 | │ └── utils # Plain old JS objects (POJOs). Pure logic. No framework specific code here.
47 | ├── tools # Node scripts that run build related tools
48 | │ ├── setup # **NEEDS DOCUMENTATION**
49 | │ │ ├── setup.js # **NEEDS DOCUMENTATION**
50 | │ │ ├── setupMessage.js # **NEEDS DOCUMENTATION**
51 | │ │ └── setupPrompts.js # **NEEDS DOCUMENTATION**
52 | │ ├── build.js # Runs the production build
53 | │ ├── chalkConfig.js # Centralized configuration for chalk (adds color to console statements)
54 | │ ├── distServer.js # Starts webserver and opens final built app that's in dist in your default browser
55 | │ ├── nodeVersionCheck.js # **NEEDS DOCUMENTATION**
56 | │ ├── removeDemo.js # **NEEDS DOCUMENTATION**
57 | │ ├── srcServer.js # Starts dev webserver with hot reloading and opens your app in your default browser
58 | │ ├── startMessage.js # **NEEDS DOCUMENTATION**
59 | │ └── testSetup.js # Configures mocha
60 | ├── webpack.config.dev.js # Configures webpack for development builds
61 | └── webpack.config.prod.js # Configures webpack for production builds
62 | ```
63 |
64 | ### What are the dependencies in package.json used for?
65 | | **Dependency** | **Use** |
66 | |----------|-------|
67 | |autoprefixer | Automatically adds vendor prefixes, using data from Can I Use. |
68 | |connect-history-api-fallback | Support reloading deep links |
69 | |object-assign | Polyfill for Object.assign |
70 | |react|React library |
71 | |react-dom|React library for DOM rendering |
72 | |react-redux|Redux library for connecting React components to Redux |
73 | |react-router|React library for routing |
74 | |redux|Library for unidirectional data flows |
75 | |redux-thunk|Middleware for redux that allows actions to be declared as functions |
76 | |babel-cli|Babel Command line interface |
77 | |babel-core|Babel Core for transpiling the new JavaScript to old |
78 | |babel-loader|Adds Babel support to Webpack |
79 | |babel-plugin-react-display-name| Add displayName to React.createClass calls |
80 | |babel-plugin-transform-react-constant-elements | Performance optimization: Hoists the creation of elements that are fully static to the top level. reduces calls to React.createElement and the resulting memory allocations. [More info](https://medium.com/doctolib-engineering/improve-react-performance-with-babel-16f1becfaa25#.2wbkg8g4d) |
81 | |babel-preset-latest|Babel preset for ES2015, ES2016 and ES2017|
82 | |babel-preset-react-hmre|Hot reloading preset for Babel|
83 | |babel-preset-react| Add JSX support to Babel |
84 | |babel-preset-stage-1| Include stage 1 feature support in Babel |
85 | |browser-sync| Supports synchronized testing on multiple devices and serves local app on public URL |
86 | |chai|Assertion library for use with Mocha|
87 | |chalk|Adds color support to terminal |
88 | |cross-env|Cross-environment friendly way to handle environment variables|
89 | |css-loader|Add CSS support to Webpack|
90 | |enzyme|Simplified JavaScript Testing utilities for React|
91 | |eslint|Lints JavaScript |
92 | |eslint-loader|Adds ESLint support to Webpack |
93 | |eslint-plugin-react|Adds additional React-related rules to ESLint|
94 | |eslint-watch|Wraps ESLint to provide file watch support and enhanced command line output|
95 | |extract-text-webpack-plugin| Extracts CSS into separate file for production build |
96 | |file-loader| Adds file loading support to Webpack |
97 | |html-webpack-plugin|Generates custom index.html for each environment as part of webpack build|
98 | |mocha| JavaScript testing library |
99 | |node-sass| Adds SASS support to Webpack |
100 | |npm-run-all| Run multiple scripts at the same time |
101 | |postcss-loader| Adds PostCSS support to Webpack |
102 | |react-addons-test-utils| Adds React TestUtils |
103 | |rimraf|Delete files |
104 | |sass-loader| Adds Sass support to Webpack|
105 | |sinon| Standalone test spies, stubs and mocks for JavaScript |
106 | |sinon-chai| Extends Chai with assertions for the Sinon.JS mocking framework|
107 | |style-loader| Add Style support to Webpack |
108 | |webpack| Bundler with plugin system and integrated development server |
109 | |webpack-dev-middleware| Used to integrate Webpack with Browser-sync |
110 | |webpack-hot-middleware| Use to integrate Webpack's hot reloading support with Browser-sync |
111 | |webpack-md5-hash| Hash bundles, and use the hash for the filename so that the filename only changes when contents change|
112 | |yargs| Easily parse command-line arguments |
113 |
114 | ### Where are the files being served from when I run `npm start`?
115 | Webpack serves your app in memory when you run `npm start`. No physical files are written. However, the web root is /src, so you can reference files under /src in index.html. When the app is built using `npm run build`, physical files are written to /dist and the app is served from /dist.
116 |
117 | ### Where is index.html?
118 | It's generated by webpack using htmlWebpackPlugin. This plugin dynamically generates index.html based on the configuration in webpack.config. It also adds references to the JS and CSS bundles using hash-based filenames to bust cache. Separate bundles for vendor and application code are created and referencing within the generated index.html file so that vendor libraries and app code can be cached separately by the browser. The bundle filenames are based on the file's hash, so the filenames only change when the file contents change. For more information on this, read [Long-term caching of static assets with Webpack](https://medium.com/@okonetchnikov/long-term-caching-of-static-assets-with-webpack-1ecb139adb95#.4aeatmtfz) and [html-webpack-plugin](https://github.com/ampedandwired/html-webpack-plugin)
119 |
120 | ### How is Sass being converted into CSS and landing in the browser?
121 | Magic! Okay, more specifically, we're handling it differently in dev (`npm start`) vs prod (`npm run build`)
122 |
123 | When you run `npm start`:
124 |
125 | 1. The sass-loader compiles Sass into CSS
126 | 2. Webpack bundles the compiled CSS into bundle.js. Sounds odd, but it works!
127 | 3. bundle.js contains code that loads styles into the <head> of index.html via JavaScript. This is why you don't see a stylesheet reference in index.html. In fact, if you disable JavaScript in your browser, you'll see the styles don't load either.
128 |
129 | The approach above supports hot reloading, which is great for development. However, it also create a flash of unstyled content on load because you have to wait for the JavaScript to parse and load styles before they're applied. So for the production build, we use a different approach:
130 |
131 | When you run `npm run build`:
132 |
133 | 1. The sass-loader compiles Sass into CSS
134 | 2. The [extract-text-webpack-plugin](https://github.com/webpack/extract-text-webpack-plugin) extracts the compiled Sass into styles.css
135 | 3. buildHtml.js adds a reference to the stylesheet to the head of index.html.
136 |
137 | For both of the above methods, a separate sourcemap is generated for debugging Sass in [compatible browsers](http://thesassway.com/intermediate/using-source-maps-with-sass).
138 |
139 | ### I don't like the magic you just described above. I simply want to use a CSS file.
140 | No problem. Reference your CSS file in index.html, and add a step to the build process to copy your CSS file over to the same relative location /dist as part of the build step. But be forwarned, you lose style hot reloading with this approach.
141 |
142 | ### I just want an empty starter kit.
143 | This starter kit includes an example app so you can see how everything hangs together on a real app. When you're done reviewing it, run this to remove the demo app:
144 |
145 | `npm run remove-demo`
146 |
147 | Don't want to use Redux? See the next question for some steps on removing Redux.
148 |
149 | ### Do I have to use Redux?
150 | Nope. Redux is useful for applications with more complex data flows. If your app is simple, Redux is overkill. Remove Redux like this:
151 |
152 | 1. Run `npm run remove-demo`
153 | 2. Uninstall Redux related packages: `npm uninstall redux react-redux redux-thunk`
154 | 3. Create a new empty component in /components.
155 | 4. Call render on the new top level component you created in step 3 in src/index.js.
156 |
157 | ### How do I remove React Router?
158 | 1. Uninstall React Router and routing related packages: `npm uninstall --save react-router`
159 | 2. Delete the following files: `src/routes.js`
160 | 3. Remove `import { Link, IndexLink } from 'react-router';` from top of `src/components/App.js`, add a reference to `src/components/FuelSavingsForm.js`, and replace body of (implicit) render with this: ``.
161 |
162 | ### How do I deploy this?
163 | `npm run build`. This will build the project for production. It does the following:
164 | * Minifies all JS
165 | * Sets NODE_ENV to prod so that React is built in production mode
166 | * Places the resulting built project files into /dist. (This is the folder you'll expose to the world).
167 |
168 | ### Why are test files placed alongside the file under test (instead of centralized)?
169 | Streamlined automated testing is a core feature of this starter kit. All tests are placed in files that end in .spec.js. Spec files are placed in the same directory as the file under test. Why?
170 | + The existence of tests is highly visible. If a corresponding .spec file hasn't been created, it's obvious.
171 | + Easy to open since they're in the same folder as the file being tested.
172 | + Easy to create new test files when creating new source files.
173 | + Short import paths are easy to type and less brittle.
174 | + As files are moved, it's easy to move tests alongside.
175 |
176 | That said, you can of course place your tests under /test instead, which is the Mocha default. If you do, you can simplify the test script to no longer specify the path. Then Mocha will simply look in /test to find your spec files.
177 |
178 | ### How do I debug?
179 | Since browsers don't currently support ES6, we're using Babel to compile our ES6 down to ES5. This means the code that runs in the browser looks different than what we wrote. But good news, a [sourcemap](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/) is generated to enable easy debugging. This means your original JS source will be displayed in your browser's dev console.
180 | *Note:* When you run `npm start`, no JS is minified. Why? Because minifying slows the build. So JS is only minified when you run the `npm run build` script. See [more on building for production below](https://github.com/coryhouse/react-slingshot#how-do-i-deploy-this).
181 |
182 | Also note that no actual physical files are written to the filesystem during the dev build. **For performance, all files exist in memory when served from the webpack server.**. Physical files are only written when you run `npm run build`.
183 |
184 | **Tips for debugging via sourcemaps:**
185 |
186 | 1. Browsers vary in the way they allow you to view the original source. Chrome automatically shows the original source if a sourcemap is available. Safari, in contrast, will display the minified source and you'll [have to cmd+click on a given line to be taken to the original source](http://stackoverflow.com/questions/19550060/how-do-i-toggle-source-mapping-in-safari-7).
187 | 2. Do **not** enable serving files from your filesystem in Chrome dev tools. If you do, Chrome (and perhaps other browsers) may not show you the latest version of your code after you make a source code change. Instead **you must close the source view tab you were using and reopen it to see the updated source code**. It appears Chrome clings to the old sourcemap until you close and reopen the source view tab. To clarify, you don't have to close the actual tab that is displaying the app, just the tab in the console that's displaying the source file that you just changed.
188 | 3. If the latest source isn't displaying the console, force a refresh. Sometimes Chrome seems to hold onto a previous version of the sourcemap which will cause you to see stale code.
189 |
190 | #### Debugging in Visual Studio Code:
191 | * Install the [Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) extension.
192 | * Follow the instructions on how to [configure debugging in Visual Studio code](https://github.com/Microsoft/vscode-chrome-debug/blob/master/README.md#using-the-debugger).
193 |
194 | Don't see your favorite code editor debugging configuration here? Submit a PR and we'll be glad to add it to the FAQ.md.
195 |
196 | ### Why does the build use npm scripts instead of Gulp or Grunt?
197 | In short, Gulp is an unnecessary abstraction that creates more problems than it solves. [Here's why](https://medium.com/@housecor/why-i-left-gulp-and-grunt-for-npm-scripts-3d6853dd22b8#.vtaziro8n).
198 |
199 | ### Why does package.json reference the exact version?
200 | This assures that the build won't break when some new version is released. Unfortunately, many package authors don't properly honor [Semantic Versioning](http://semver.org), so instead, as new versions are released, I'll test them and then introduce them into the starter kit. But yes, this means when you do `npm update` no new dependencies will be pulled down. You'll have to update package.json with the new version manually.
201 |
202 | ### How do I handle images?
203 | Via Webpack's file loader. Example:
204 |
205 | ```
206 |
207 |
208 | ```
209 |
210 | Webpack will then intelligently handle your image for you. For the production build, it will copy the physical file to /dist, give it a unique filename, and insert the appropriate path in your image tag.
211 |
212 | ### I'm getting an error when running npm install: Failed to locate "CL.exe"
213 | On Windows, you need to install extra dependencies for browser-sync to build and install successfully. Follow the getting started steps above to assure you have the necessary dependencies on your machine.
214 |
215 | ### I can't access the external URL for Browsersync
216 | To hit the external URL, all devices must be on the same LAN. So this may mean your dev machine needs to be on the same Wifi as the mobile devices you're testing.
217 |
218 | ### What about the Redux Devtools?
219 | Install the [Redux devtools extension](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en) in Chrome Developer Tools. If you're interested in running Redux dev tools cross-browser, Barry Staes created a [branch with the devtools incorporated](https://github.com/coryhouse/react-slingshot/pull/27).
220 |
221 | ### Hot reloading isn't working!
222 | Hot reloading doesn't always play nicely with stateless functional components at this time. [This is a known limitation that is currently being worked](https://github.com/gaearon/babel-plugin-react-transform/issues/57). To avoid issues with hot reloading for now, use a traditional class-based React component at the top of your component hierarchy.
223 |
224 | ### How do I setup code coverage reporting?
225 | Using the `npm run test:cover` command to run the tests, building a code coverage report. The report is written to `coverage/index.html`. Slingshot provides a script for this:
226 |
227 | ```bash
228 | npm run open:cover
229 | ```
230 |
231 | You can add code coverage metrics to your `README.md` file and pull by integrating with [Coveralls](https://coveralls.io/).
232 |
233 | 1. Sign in to Coveralls with your GitHub account.
234 | 2. Authorise Coveralls to access your repositories.
235 | 3. Choose 'Add Repo' and select your repo.
236 |
237 | That's it! Travis will now execute the `npm run test:cover:travis` script after a successful build, which will write the coverage report in the standard lcov format and send it directly to Coveralls. The environment variables provided for travis jobs are used to automatically target the correct Coveralls project, as long as it is set up as described above.
238 |
239 | You can get the badge from the Coveralls website.
240 |
241 | ###What about TypeScript?
242 | Here's a [fork with TS support](https://github.com/typescriptcrew/ts-react-slingshot):
243 |
244 |
--------------------------------------------------------------------------------
/book-store/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "book-store",
3 | "version": "0.1.0",
4 | "description": "A basic online book store built over react-redux",
5 | "engines": {
6 | "npm": ">=3"
7 | },
8 | "scripts": {
9 | "preinstall": "node tools/nodeVersionCheck.js",
10 | "setup": "node tools/setup/setupMessage.js && npm install && node tools/setup/setup.js",
11 | "remove-demo": "babel-node tools/removeDemo.js",
12 | "start-message": "babel-node tools/startMessage.js",
13 | "prestart": "npm-run-all --parallel start-message remove-dist",
14 | "start": "npm-run-all --parallel test:watch open:src lint:watch",
15 | "open:src": "babel-node tools/srcServer.js",
16 | "open:dist": "babel-node tools/distServer.js",
17 | "lint": "esw webpack.config.* src tools --color",
18 | "lint:watch": "npm run lint -- --watch",
19 | "clean-dist": "npm run remove-dist && mkdir dist",
20 | "remove-dist": "rimraf ./dist",
21 | "prebuild": "npm run clean-dist && npm run lint && npm run test",
22 | "build": "babel-node tools/build.js && npm run open:dist",
23 | "test": "mocha tools/testSetup.js \"./{,!(node_modules)/**/}*.spec.js\" --reporter progress",
24 | "test:cover": "babel-node node_modules/isparta/bin/isparta cover --root src --report html node_modules/mocha/bin/_mocha -- --require ./tools/testSetup.js \"./{,!(node_modules)/**/}*.spec.js\" --reporter progress",
25 | "test:cover:travis": "babel-node node_modules/isparta/bin/isparta cover --root src --report lcovonly _mocha -- --require ./tools/testSetup.js \"./{,!(node_modules)/**/}*.spec.js\" && cat ./coverage/lcov.info | node_modules/coveralls/bin/coveralls.js",
26 | "test:watch": "npm run test -- --watch",
27 | "open:cover": "npm run test:cover && open coverage/index.html"
28 | },
29 | "author": "ajayns",
30 | "license": "MIT",
31 | "dependencies": {
32 | "bootstrap": "3.3.7",
33 | "object-assign": "4.1.0",
34 | "react": "15.3.2",
35 | "react-dom": "15.3.2",
36 | "react-redux": "4.4.5",
37 | "react-router": "2.8.1",
38 | "react-router-redux": "4.0.6",
39 | "redux": "3.6.0",
40 | "redux-thunk": "2.1.0"
41 | },
42 | "devDependencies": {
43 | "autoprefixer": "6.5.1",
44 | "babel-cli": "6.16.0",
45 | "babel-core": "6.17.0",
46 | "babel-eslint": "7.0.0",
47 | "babel-loader": "6.2.5",
48 | "babel-plugin-react-display-name": "2.0.0",
49 | "babel-plugin-transform-react-constant-elements": "6.9.1",
50 | "babel-plugin-transform-react-remove-prop-types": "0.2.10",
51 | "babel-preset-latest": "6.16.0",
52 | "babel-preset-react": "6.16.0",
53 | "babel-preset-react-hmre": "1.1.1",
54 | "babel-preset-stage-1": "6.16.0",
55 | "babel-register": "6.16.3",
56 | "browser-sync": "2.17.5",
57 | "chai": "3.5.0",
58 | "chalk": "1.1.3",
59 | "connect-history-api-fallback": "1.3.0",
60 | "coveralls": "2.11.14",
61 | "cross-env": "3.1.3",
62 | "css-loader": "0.25.0",
63 | "enzyme": "2.5.1",
64 | "eslint": "3.8.1",
65 | "eslint-plugin-import": "2.0.1",
66 | "eslint-plugin-jsx-a11y": "2.2.3",
67 | "eslint-plugin-react": "6.4.1",
68 | "eslint-watch": "2.1.14",
69 | "extract-text-webpack-plugin": "1.0.1",
70 | "file-loader": "0.9.0",
71 | "html-webpack-plugin": "2.24.0",
72 | "isparta": "4.0.0",
73 | "istanbul": "0.4.4",
74 | "json-loader": "0.5.4",
75 | "mocha": "3.1.2",
76 | "mockdate": "1.0.4",
77 | "node-sass": "3.10.1",
78 | "npm-run-all": "3.1.1",
79 | "open": "0.0.5",
80 | "postcss-loader": "1.0.0",
81 | "prompt": "1.0.0",
82 | "react-addons-test-utils": "15.3.2",
83 | "redux-immutable-state-invariant": "1.2.4",
84 | "replace": "0.3.0",
85 | "rimraf": "2.5.4",
86 | "sass-loader": "4.0.2",
87 | "sinon": "1.17.6",
88 | "sinon-chai": "2.8.0",
89 | "style-loader": "0.13.1",
90 | "url-loader": "0.5.7",
91 | "webpack": "1.13.2",
92 | "webpack-bundle-analyzer": "1.4.1",
93 | "webpack-dev-middleware": "1.8.4",
94 | "webpack-hot-middleware": "2.13.0",
95 | "webpack-md5-hash": "0.0.5"
96 | },
97 | "keywords": [],
98 | "repository": {
99 | "type": "git",
100 | "url": ""
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/book-store/src/actions/bookActions.js:
--------------------------------------------------------------------------------
1 | export const createBook = (book) => {
2 | // Return action
3 | return {
4 | // Type of action
5 | type: 'CREATE_BOOK',
6 | // Payload
7 | book
8 | };
9 | };
--------------------------------------------------------------------------------
/book-store/src/components/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const App = (props) => {
5 | return (
6 |
47 | )
48 | }
49 | }
50 |
51 | // ========================================
52 |
53 | ReactDOM.render(
54 | ,
55 | document.getElementById('root')
56 | );
57 |
--------------------------------------------------------------------------------
/property-finder/App.js:
--------------------------------------------------------------------------------
1 | import { StackNavigator } from 'react-navigation';
2 | import SearchPage from './screens/SearchPage';
3 |
4 | const App = StackNavigator({
5 | Home: { screen: SearchPage },
6 | Results: { screen: SearchResults }
7 | });
8 |
--------------------------------------------------------------------------------
/property-finder/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import App from './App';
3 |
4 | import renderer from 'react-test-renderer';
5 |
6 | it('renders without crashing', () => {
7 | const rendered = renderer.create().toJSON();
8 | expect(rendered).toBeTruthy();
9 | });
10 |
--------------------------------------------------------------------------------
/property-finder/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React Native App](https://github.com/react-community/create-react-native-app).
2 |
3 | Below you'll find information about performing common tasks. The most recent version of this guide is available [here](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/README.md).
4 |
5 | ## Table of Contents
6 |
7 | * [Updating to New Releases](#updating-to-new-releases)
8 | * [Available Scripts](#available-scripts)
9 | * [npm start](#npm-start)
10 | * [npm test](#npm-test)
11 | * [npm run ios](#npm-run-ios)
12 | * [npm run android](#npm-run-android)
13 | * [npm run eject](#npm-run-eject)
14 | * [Writing and Running Tests](#writing-and-running-tests)
15 | * [Environment Variables](#environment-variables)
16 | * [Configuring Packager IP Address](#configuring-packager-ip-address)
17 | * [Adding Flow](#adding-flow)
18 | * [Customizing App Display Name and Icon](#customizing-app-display-name-and-icon)
19 | * [Sharing and Deployment](#sharing-and-deployment)
20 | * [Publishing to Expo's React Native Community](#publishing-to-expos-react-native-community)
21 | * [Building an Expo "standalone" app](#building-an-expo-standalone-app)
22 | * [Ejecting from Create React Native App](#ejecting-from-create-react-native-app)
23 | * [Build Dependencies (Xcode & Android Studio)](#build-dependencies-xcode-android-studio)
24 | * [Should I Use ExpoKit?](#should-i-use-expokit)
25 | * [Troubleshooting](#troubleshooting)
26 | * [Networking](#networking)
27 | * [iOS Simulator won't open](#ios-simulator-wont-open)
28 | * [QR Code does not scan](#qr-code-does-not-scan)
29 |
30 | ## Updating to New Releases
31 |
32 | You should only need to update the global installation of `create-react-native-app` very rarely, ideally never.
33 |
34 | Updating the `react-native-scripts` dependency of your app should be as simple as bumping the version number in `package.json` and reinstalling your project's dependencies.
35 |
36 | Upgrading to a new version of React Native requires updating the `react-native`, `react`, and `expo` package versions, and setting the correct `sdkVersion` in `app.json`. See the [versioning guide](https://github.com/react-community/create-react-native-app/blob/master/VERSIONS.md) for up-to-date information about package version compatibility.
37 |
38 | ## Available Scripts
39 |
40 | If Yarn was installed when the project was initialized, then dependencies will have been installed via Yarn, and you should probably use it to run these commands as well. Unlike dependency installation, command running syntax is identical for Yarn and NPM at the time of this writing.
41 |
42 | ### `npm start`
43 |
44 | Runs your app in development mode.
45 |
46 | Open it in the [Expo app](https://expo.io) on your phone to view it. It will reload if you save edits to your files, and you will see build errors and logs in the terminal.
47 |
48 | Sometimes you may need to reset or clear the React Native packager's cache. To do so, you can pass the `--reset-cache` flag to the start script:
49 |
50 | ```
51 | npm start -- --reset-cache
52 | # or
53 | yarn start -- --reset-cache
54 | ```
55 |
56 | #### `npm test`
57 |
58 | Runs the [jest](https://github.com/facebook/jest) test runner on your tests.
59 |
60 | #### `npm run ios`
61 |
62 | Like `npm start`, but also attempts to open your app in the iOS Simulator if you're on a Mac and have it installed.
63 |
64 | #### `npm run android`
65 |
66 | Like `npm start`, but also attempts to open your app on a connected Android device or emulator. Requires an installation of Android build tools (see [React Native docs](https://facebook.github.io/react-native/docs/getting-started.html) for detailed setup). We also recommend installing Genymotion as your Android emulator. Once you've finished setting up the native build environment, there are two options for making the right copy of `adb` available to Create React Native App:
67 |
68 | ##### Using Android Studio's `adb`
69 |
70 | 1. Make sure that you can run adb from your terminal.
71 | 2. Open Genymotion and navigate to `Settings -> ADB`. Select “Use custom Android SDK tools” and update with your [Android SDK directory](https://stackoverflow.com/questions/25176594/android-sdk-location).
72 |
73 | ##### Using Genymotion's `adb`
74 |
75 | 1. Find Genymotion’s copy of adb. On macOS for example, this is normally `/Applications/Genymotion.app/Contents/MacOS/tools/`.
76 | 2. Add the Genymotion tools directory to your path (instructions for [Mac](http://osxdaily.com/2014/08/14/add-new-path-to-path-command-line/), [Linux](http://www.computerhope.com/issues/ch001647.htm), and [Windows](https://www.howtogeek.com/118594/how-to-edit-your-system-path-for-easy-command-line-access/)).
77 | 3. Make sure that you can run adb from your terminal.
78 |
79 | #### `npm run eject`
80 |
81 | This will start the process of "ejecting" from Create React Native App's build scripts. You'll be asked a couple of questions about how you'd like to build your project.
82 |
83 | **Warning:** Running eject is a permanent action (aside from whatever version control system you use). An ejected app will require you to have an [Xcode and/or Android Studio environment](https://facebook.github.io/react-native/docs/getting-started.html) set up.
84 |
85 | ## Customizing App Display Name and Icon
86 |
87 | You can edit `app.json` to include [configuration keys](https://docs.expo.io/versions/latest/guides/configuration.html) under the `expo` key.
88 |
89 | To change your app's display name, set the `expo.name` key in `app.json` to an appropriate string.
90 |
91 | To set an app icon, set the `expo.icon` key in `app.json` to be either a local path or a URL. It's recommended that you use a 512x512 png file with transparency.
92 |
93 | ## Writing and Running Tests
94 |
95 | This project is set up to use [jest](https://facebook.github.io/jest/) for tests. You can configure whatever testing strategy you like, but jest works out of the box. Create test files in directories called `__tests__` or with the `.test` extension to have the files loaded by jest. See the [the template project](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/App.test.js) for an example test. The [jest documentation](https://facebook.github.io/jest/docs/en/getting-started.html) is also a wonderful resource, as is the [React Native testing tutorial](https://facebook.github.io/jest/docs/en/tutorial-react-native.html).
96 |
97 | ## Environment Variables
98 |
99 | You can configure some of Create React Native App's behavior using environment variables.
100 |
101 | ### Configuring Packager IP Address
102 |
103 | When starting your project, you'll see something like this for your project URL:
104 |
105 | ```
106 | exp://192.168.0.2:19000
107 | ```
108 |
109 | The "manifest" at that URL tells the Expo app how to retrieve and load your app's JavaScript bundle, so even if you load it in the app via a URL like `exp://localhost:19000`, the Expo client app will still try to retrieve your app at the IP address that the start script provides.
110 |
111 | In some cases, this is less than ideal. This might be the case if you need to run your project inside of a virtual machine and you have to access the packager via a different IP address than the one which prints by default. In order to override the IP address or hostname that is detected by Create React Native App, you can specify your own hostname via the `REACT_NATIVE_PACKAGER_HOSTNAME` environment variable:
112 |
113 | Mac and Linux:
114 |
115 | ```
116 | REACT_NATIVE_PACKAGER_HOSTNAME='my-custom-ip-address-or-hostname' npm start
117 | ```
118 |
119 | Windows:
120 | ```
121 | set REACT_NATIVE_PACKAGER_HOSTNAME='my-custom-ip-address-or-hostname'
122 | npm start
123 | ```
124 |
125 | The above example would cause the development server to listen on `exp://my-custom-ip-address-or-hostname:19000`.
126 |
127 | ## Adding Flow
128 |
129 | Flow is a static type checker that helps you write code with fewer bugs. Check out this [introduction to using static types in JavaScript](https://medium.com/@preethikasireddy/why-use-static-types-in-javascript-part-1-8382da1e0adb) if you are new to this concept.
130 |
131 | React Native works with [Flow](http://flowtype.org/) out of the box, as long as your Flow version matches the one used in the version of React Native.
132 |
133 | To add a local dependency to the correct Flow version to a Create React Native App project, follow these steps:
134 |
135 | 1. Find the Flow `[version]` at the bottom of the included [.flowconfig](.flowconfig)
136 | 2. Run `npm install --save-dev flow-bin@x.y.z` (or `yarn add --dev flow-bin@x.y.z`), where `x.y.z` is the .flowconfig version number.
137 | 3. Add `"flow": "flow"` to the `scripts` section of your `package.json`.
138 | 4. Add `// @flow` to any files you want to type check (for example, to `App.js`).
139 |
140 | Now you can run `npm run flow` (or `yarn flow`) to check the files for type errors.
141 | You can optionally use a [plugin for your IDE or editor](https://flow.org/en/docs/editors/) for a better integrated experience.
142 |
143 | To learn more about Flow, check out [its documentation](https://flow.org/).
144 |
145 | ## Sharing and Deployment
146 |
147 | Create React Native App does a lot of work to make app setup and development simple and straightforward, but it's very difficult to do the same for deploying to Apple's App Store or Google's Play Store without relying on a hosted service.
148 |
149 | ### Publishing to Expo's React Native Community
150 |
151 | Expo provides free hosting for the JS-only apps created by CRNA, allowing you to share your app through the Expo client app. This requires registration for an Expo account.
152 |
153 | Install the `exp` command-line tool, and run the publish command:
154 |
155 | ```
156 | $ npm i -g exp
157 | $ exp publish
158 | ```
159 |
160 | ### Building an Expo "standalone" app
161 |
162 | You can also use a service like [Expo's standalone builds](https://docs.expo.io/versions/latest/guides/building-standalone-apps.html) if you want to get an IPA/APK for distribution without having to build the native code yourself.
163 |
164 | ### Ejecting from Create React Native App
165 |
166 | If you want to build and deploy your app yourself, you'll need to eject from CRNA and use Xcode and Android Studio.
167 |
168 | This is usually as simple as running `npm run eject` in your project, which will walk you through the process. Make sure to install `react-native-cli` and follow the [native code getting started guide for React Native](https://facebook.github.io/react-native/docs/getting-started.html).
169 |
170 | #### Should I Use ExpoKit?
171 |
172 | If you have made use of Expo APIs while working on your project, then those API calls will stop working if you eject to a regular React Native project. If you want to continue using those APIs, you can eject to "React Native + ExpoKit" which will still allow you to build your own native code and continue using the Expo APIs. See the [ejecting guide](https://github.com/react-community/create-react-native-app/blob/master/EJECTING.md) for more details about this option.
173 |
174 | ## Troubleshooting
175 |
176 | ### Networking
177 |
178 | If you're unable to load your app on your phone due to a network timeout or a refused connection, a good first step is to verify that your phone and computer are on the same network and that they can reach each other. Create React Native App needs access to ports 19000 and 19001 so ensure that your network and firewall settings allow access from your device to your computer on both of these ports.
179 |
180 | Try opening a web browser on your phone and opening the URL that the packager script prints, replacing `exp://` with `http://`. So, for example, if underneath the QR code in your terminal you see:
181 |
182 | ```
183 | exp://192.168.0.1:19000
184 | ```
185 |
186 | Try opening Safari or Chrome on your phone and loading
187 |
188 | ```
189 | http://192.168.0.1:19000
190 | ```
191 |
192 | and
193 |
194 | ```
195 | http://192.168.0.1:19001
196 | ```
197 |
198 | If this works, but you're still unable to load your app by scanning the QR code, please open an issue on the [Create React Native App repository](https://github.com/react-community/create-react-native-app) with details about these steps and any other error messages you may have received.
199 |
200 | If you're not able to load the `http` URL in your phone's web browser, try using the tethering/mobile hotspot feature on your phone (beware of data usage, though), connecting your computer to that WiFi network, and restarting the packager.
201 |
202 | ### iOS Simulator won't open
203 |
204 | If you're on a Mac, there are a few errors that users sometimes see when attempting to `npm run ios`:
205 |
206 | * "non-zero exit code: 107"
207 | * "You may need to install Xcode" but it is already installed
208 | * and others
209 |
210 | There are a few steps you may want to take to troubleshoot these kinds of errors:
211 |
212 | 1. Make sure Xcode is installed and open it to accept the license agreement if it prompts you. You can install it from the Mac App Store.
213 | 2. Open Xcode's Preferences, the Locations tab, and make sure that the `Command Line Tools` menu option is set to something. Sometimes when the CLI tools are first installed by Homebrew this option is left blank, which can prevent Apple utilities from finding the simulator. Make sure to re-run `npm/yarn run ios` after doing so.
214 | 3. If that doesn't work, open the Simulator, and under the app menu select `Reset Contents and Settings...`. After that has finished, quit the Simulator, and re-run `npm/yarn run ios`.
215 |
216 | ### QR Code does not scan
217 |
218 | If you're not able to scan the QR code, make sure your phone's camera is focusing correctly, and also make sure that the contrast on the two colors in your terminal is high enough. For example, WebStorm's default themes may [not have enough contrast](https://github.com/react-community/create-react-native-app/issues/49) for terminal QR codes to be scannable with the system barcode scanners that the Expo app uses.
219 |
220 | If this causes problems for you, you may want to try changing your terminal's color theme to have more contrast, or running Create React Native App from a different terminal. You can also manually enter the URL printed by the packager script in the Expo app's search bar to load it manually.
221 |
--------------------------------------------------------------------------------
/property-finder/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "PropertyFinder",
3 | "displayName": "PropertyFinder",
4 | "expo": {
5 | "sdkVersion": "23.0.0"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/property-finder/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "property-finder",
3 | "version": "0.1.0",
4 | "private": true,
5 | "devDependencies": {
6 | "jest-expo": "23.0.0",
7 | "react-native-scripts": "1.9.0",
8 | "react-test-renderer": "16.0.0"
9 | },
10 | "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
11 | "scripts": {
12 | "start": "react-native-scripts start",
13 | "eject": "react-native-scripts eject",
14 | "android": "react-native-scripts android",
15 | "ios": "react-native-scripts ios",
16 | "test": "node node_modules/jest/bin/jest.js --watch"
17 | },
18 | "jest": {
19 | "preset": "jest-expo"
20 | },
21 | "dependencies": {
22 | "expo": "^23.0.4",
23 | "react": "16.0.0",
24 | "react-native": "0.50.3",
25 | "react-navigation": "^1.0.0-beta.27"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/property-finder/resources/house-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajayns/react-projects/f2529c7d6cbb78aaeba90fdaa48f663b48cdb8f8/property-finder/resources/house-icon.png
--------------------------------------------------------------------------------
/property-finder/screens/SearchPage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | StyleSheet,
4 | Text,
5 | TextInput,
6 | View,
7 | Button,
8 | ActivityIndicator,
9 | Image
10 | } from 'react-native';
11 |
12 | const generateQuery = (key, value, pageNumber) => {
13 | const data = {
14 | country: 'uk',
15 | pretty: '1',
16 | encoding: 'json',
17 | listing_type: 'buy',
18 | action: 'search_listings',
19 | page: pageNumber,
20 | };
21 |
22 | data[key] = value;
23 |
24 | const queryString = Object.keys(data)
25 | .map(key => `${key}=${encodeURIComponent(data[key])}`)
26 | .join('&');
27 |
28 | return 'https://api.nestoria.co.uk/api?' + queryString;
29 | }
30 |
31 | export default class SearchPage extends React.Component {
32 | static navigationOptions = {
33 | title: 'Property Finder',
34 | };
35 |
36 | constructor(props) {
37 | super(props);
38 | this.state = {
39 | searchString: 'london',
40 | isLoading: false,
41 | message: ''
42 | };
43 | }
44 |
45 | searchQuery(q) {
46 | this.setState({
47 | isLoading: true
48 | })
49 | fetch(query)
50 | .then(res => res.json())
51 | .then(json => this.handleResponse(json.response))
52 | .catch(err =>
53 | this.setState({
54 | isLoading: false,
55 | message: 'Something bad happened'
56 | })
57 | )
58 | }
59 |
60 | handleResponse(res) {
61 | this.setState({
62 | isLoading: false,
63 | message: ''
64 | });
65 |
66 | if (response.application_response_code.substr(0, 1) === '1') {
67 | this.props.navigation.navigate(
68 | 'Results',
69 | {listings: response.listings}
70 | );
71 | } else {
72 | this.setState({ message: 'Location not recognized; please try again.' });
73 | }
74 | }
75 |
76 | onSearch() {
77 | const query = generateQuery('place_name', this.state.searchString, 1);
78 | this.searchQuery(query);
79 | }
80 |
81 | handleSearchChange(e) {
82 | this.setState({
83 | searchString: e.nativeEvent.text
84 | })
85 | }
86 |
87 | render() {
88 |
89 | if(this.state.isLoading) {
90 | return ;
91 | } else {
92 | return (
93 |
94 | Search for houses to buy!
95 |
96 |
102 |
107 |
108 |
109 | {this.state.message}
110 |
111 | );
112 | }
113 | }
114 | }
115 |
116 | const styles = StyleSheet.create({
117 | container: {
118 | flex: 1,
119 | backgroundColor: '#fff',
120 | alignItems: 'center',
121 | justifyContent: 'center',
122 | },
123 | flowRight: {
124 | flexDirection: 'row',
125 | },
126 | image: {
127 | width: 240,
128 | height: 240,
129 | }
130 | });
131 |
--------------------------------------------------------------------------------
/property-finder/screens/SearchResults.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | StyleSheet,
4 | Image,
5 | View,
6 | TouchableHighlight,
7 | FlatList,
8 | Text,
9 | } from 'react-native';
10 |
11 |
12 | class ListItem extends React.PureComponent {
13 | render() {
14 | const item = this.props.item;
15 | const price = item.price_formatted.split(' ')[0];
16 | return (
17 |
18 |
19 |
20 |
21 |
22 | {price}
23 |
26 | {item.title}
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | }
35 | }
36 |
37 | export default class SearchResults extends React.Component {
38 | static navigationOptions = {
39 | title: 'Results',
40 | };
41 |
42 | keyExtractor = (item, index) => index;
43 |
44 | renderItem({ item, index }) {
45 | return (
46 |
50 | );
51 | }
52 |
53 | render() {
54 | const { params } = this.props.navigation.state;
55 | return (
56 |
61 | );
62 | }
63 | }
64 |
65 | const styles = StyleSheet.create({
66 | thumb: {
67 | width: 80,
68 | height: 80,
69 | marginRight: 10
70 | },
71 | textContainer: {
72 | flex: 1
73 | },
74 | separator: {
75 | height: 1,
76 | backgroundColor: '#dddddd'
77 | },
78 | price: {
79 | fontSize: 25,
80 | fontWeight: 'bold',
81 | color: '#48BBEC'
82 | },
83 | title: {
84 | fontSize: 20,
85 | color: '#656565'
86 | },
87 | rowContainer: {
88 | flexDirection: 'row',
89 | padding: 10
90 | },
91 | });
--------------------------------------------------------------------------------
/reddit-clone/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/reddit-clone/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reddit-clone",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "cross-fetch": "^2.1.0",
7 | "react": "^16.2.0",
8 | "react-dom": "^16.2.0",
9 | "react-redux": "^5.0.7",
10 | "react-scripts": "1.1.1",
11 | "redux": "^3.7.2",
12 | "redux-logger": "^3.0.6",
13 | "redux-thunk": "^2.2.0"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test --env=jsdom",
19 | "eject": "react-scripts eject"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/reddit-clone/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajayns/react-projects/f2529c7d6cbb78aaeba90fdaa48f663b48cdb8f8/reddit-clone/public/favicon.ico
--------------------------------------------------------------------------------
/reddit-clone/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Reddit Clone
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/reddit-clone/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/reddit-clone/src/actions.js:
--------------------------------------------------------------------------------
1 | import {
2 | SELECT_SUBREDDIT,
3 | INVALIDATE_SUBREDDIT,
4 | REQUEST_POSTS,
5 | RECEIVE_POSTS
6 | } from './constants'
7 |
8 | // Import fetch API in case of browser compatiblity issues
9 | import fetch from 'cross-fetch'
10 |
11 | // Action creators for all functionality
12 | // Basically wraps up type and subreddit selected into an object
13 |
14 | export const selectSubreddit = (subreddit) => (
15 | {
16 | type: SELECT_SUBREDDIT,
17 | subreddit
18 | }
19 | )
20 |
21 | export const invalidateSubreddit = (subreddit) => (
22 | {
23 | type: INVALIDATE_SUBREDDIT,
24 | subreddit
25 | }
26 | )
27 |
28 | const requestPosts = (subreddit) => (
29 | {
30 | type: REQUEST_POSTS,
31 | subreddit
32 | }
33 | )
34 |
35 | const recievePosts = (subreddit, json) => (
36 | {
37 | type: RECEIVE_POSTS,
38 | subreddit,
39 | posts: json.data.children.map(child => child.data),
40 | recievedAt: Date.now()
41 | }
42 | )
43 |
44 | // Helper function to fetch JSON data from Reddit API
45 | const fetchPosts = (subreddit) => {
46 | return (dispatch) => {
47 | // Dispatch requestPosts action just before attempting to fetching data
48 | dispatch(requestPosts(subreddit))
49 |
50 | // Fetch data and dispatch recievePosts if no errors
51 | // Catch shouldn't be used to handle errors as
52 | return fetch(`https://www.reddit.com/r/${subreddit}.json`)
53 | .then(
54 | response => response.json(),
55 | error => console.log('An error occured', error)
56 | )
57 | .then(
58 | json => dispatch(recievePosts(subreddit, json))
59 | )
60 | }
61 | }
62 |
63 | const shouldFetchPosts = (state, subreddit) => {
64 | // Get posts by subreddit from state
65 | const posts = state.postsBySubreddit[subreddit]
66 |
67 | // Should fetch posts if no posts exists and not fetching already
68 | // If posts exists and not fetching then depends on if posts have invalidated
69 | if (!posts) {
70 | return true
71 | } else if (posts.isFetching) {
72 | return false
73 | } else {
74 | return posts.didInvalidate
75 | }
76 | }
77 |
78 | export const fetchPostsIfNeeded = (subreddit) => {
79 | return (dispatch, getState) => {
80 | if (shouldFetchPosts(getState(), subreddit)) {
81 | return dispatch(fetchPosts(subreddit))
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/reddit-clone/src/actions/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | SELECT_SUBREDDIT,
3 | INVALIDATE_SUBREDDIT,
4 | REQUEST_POSTS,
5 | RECEIVE_POSTS
6 | } from `../constants/action-types`
7 |
8 | import fetch from 'cross-fetch'
9 |
10 | export const selectSubreddit = (subreddit) => (
11 | {
12 | type: SELECT_SUBREDDIT,
13 | subreddit
14 | }
15 | )
16 |
17 | export const invalidateSubreddit = (subreddit) => (
18 | {
19 | type: INVALIDATE_SUBREDDIT,
20 | subreddit
21 | }
22 | )
23 |
24 | const requestPosts = (subreddit) => (
25 | {
26 | type: REQUEST_POSTS,
27 | subreddit
28 | }
29 | )
30 |
31 | const recievePosts = (subreddit, json) => (
32 | {
33 | type: RECEIVE_POSTS,
34 | subreddit,
35 | posts: json.data.children.map(child => child.data),
36 | recievedAt: Date.now()
37 | }
38 | )
39 |
40 | const fetchPosts = (subreddit) => {
41 | return (dispatch) => {
42 | dispatch(requestPosts(subreddit))
43 |
44 | return fetch(`https://www.reddit.com/r/${subreddit}.json`)
45 | .then(
46 | response => response.json(),
47 | error => console.log('An error occured', error)
48 | )
49 | .then(
50 | json => dispatch(recievePosts(subreddit, json))
51 | )
52 | }
53 | }
54 |
55 | const shouldFetchPosts = (state, subreddit) => {
56 | const posts = state.postsBySubreddit[subreddit]
57 | if (!posts) {
58 | return true
59 | } else if (posts.isFetching) {
60 | return false
61 | } else {
62 | return posts.didInvalidate
63 | }
64 | }
65 |
66 | export const fetchPostsIfNeeded = (subreddit) => {
67 | return (dispatch, getState) => {
68 | if (shouldFetchPosts(getState(), subreddit)) {
69 | return dispatch(fetchPosts(subreddit))
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/reddit-clone/src/components/Picker.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | export default class Picker extends Component {
5 | render() {
6 | const { value, onChange, options } = this.props
7 |
8 | return (
9 |
10 |