├── .babelrc
├── .gitignore
├── README.md
├── index.html
├── package.json
├── src
├── actions
│ └── index.js
├── components
│ └── app.js
├── index.js
└── reducers
│ ├── index.js
│ └── users.js
├── style
└── style.css
├── test
├── components
│ └── app_test.js
└── test_helper.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015", "stage-1"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | node_modules
28 |
29 | # Optional npm cache directory
30 | .npm
31 |
32 | # Optional REPL history
33 | .node_repl_history
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ##Action Creator
2 |
3 | En primer lugar creamos la acción (en realidad es un action creator), que es donde se encuentra el código encargado de conectarse al API. Como vamos a realizar una llamada asíncrona, en lugar de devolver un simple objeto JSON, nuestro action creator va a tener que devolver una función. Esta función la va a tener que procesar un middleware, redux-thunk. Por lo tanto vamos a tener que intalar este middleware, y también vamos a instalar el módulo axios para realizar peticiones HTTP. El código de nuestro action creator quedaría definido de la siguiente forma:
4 |
5 | ```javascript
6 | import axios from 'axios'
7 |
8 | export const SHOW_USERS = 'SHOW_USERS'
9 |
10 | export function showUsers() {
11 |
12 | return (dispatch, getState) => {
13 | axios.get('http://jsonplaceholder.typicode.com/users')
14 | .then((response) => {
15 | dispatch( { type: SHOW_USERS, payload: response.data } )
16 | })
17 | }
18 |
19 | }
20 | ```
21 |
22 |
23 | ##Reducer
24 |
25 | El siguiente fichero que vamos a crear es nuestro reducer. Este fichero no tiene nada especial. Se trata de un reducer estándar:
26 |
27 | ```javascript
28 | import { SHOW_USERS } from '../actions'
29 |
30 | const initialState = {
31 | list: []
32 | }
33 |
34 | export function showUsers(state = initialState, action) {
35 |
36 | switch (action.type) {
37 | case SHOW_USERS:
38 | return Object.assign({}, state, {list: action.payload})
39 | default:
40 | return state
41 | }
42 |
43 | }
44 | ```
45 |
46 | ##Component
47 | Y por último, tenemos que modificar nuestro Componente para que se conecta a nuestro almacén de Redux y para que muestre el listado de usuarios en forma de tabla. El código del componente quedaría de la siguiente forma:
48 |
49 | ```javascript
50 | import React from 'react';
51 | import { Component } from 'react';
52 | import { connect } from 'react-redux'
53 | import { showUsers } from '../actions'
54 | import { Table } from 'react-bootstrap'
55 |
56 | class App extends Component {
57 |
58 | componentWillMount() {
59 | this.props.showUsers()
60 | }
61 |
62 | renderUsersList() {
63 | return this.props.users.map((user) => {
64 | return (
65 |
66 | {user.id} |
67 | {user.name} |
68 | {user.email} |
69 |
70 | )
71 | })
72 | }
73 |
74 | render() {
75 | return (
76 |
77 |
Users List
78 |
79 |
80 |
81 | Id |
82 | Name |
83 | Email |
84 |
85 |
86 |
87 | { this.renderUsersList() }
88 |
89 |
90 |
91 | );
92 | }
93 | }
94 |
95 | function mapStateToProps(state) {
96 | return {
97 | users: state.user.list
98 | }
99 | }
100 |
101 | export default connect(mapStateToProps, { showUsers })(App)
102 | ```
103 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redux-simple-starter",
3 | "version": "1.0.0",
4 | "description": "Simple starter package for Redux with React and Babel support",
5 | "main": "index.js",
6 | "repository": "git@github.com:StephenGrider/ReduxSimpleStarter.git",
7 | "scripts": {
8 | "start": "node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js",
9 | "test": "mocha --compilers js:babel-core/register --require ./test/test_helper.js --recursive ./test",
10 | "test:watch": "npm run test -- --watch"
11 | },
12 | "author": "",
13 | "license": "ISC",
14 | "devDependencies": {
15 | "babel-core": "^6.2.1",
16 | "babel-loader": "^6.2.0",
17 | "babel-preset-es2015": "^6.1.18",
18 | "babel-preset-react": "^6.1.18",
19 | "chai": "^3.5.0",
20 | "chai-jquery": "^2.0.0",
21 | "jquery": "^2.2.1",
22 | "jsdom": "^8.1.0",
23 | "mocha": "^2.4.5",
24 | "react-addons-test-utils": "^0.14.7",
25 | "webpack": "^1.12.9",
26 | "webpack-dev-server": "^1.14.0"
27 | },
28 | "dependencies": {
29 | "axios": "^0.11.0",
30 | "babel-preset-stage-1": "^6.1.18",
31 | "lodash": "^3.10.1",
32 | "react": "^0.14.3",
33 | "react-bootstrap": "^0.29.3",
34 | "react-dom": "^0.14.3",
35 | "react-redux": "^4.0.0",
36 | "react-router": "^2.0.1",
37 | "redux": "^3.0.4",
38 | "redux-thunk": "^2.0.1"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/actions/index.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 |
3 | export const SHOW_USERS = 'SHOW_USERS'
4 |
5 | export function showUsers() {
6 |
7 | return (dispatch, getState) => {
8 | axios.get('http://jsonplaceholder.typicode.com/users')
9 | .then((response) => {
10 | dispatch( { type: SHOW_USERS, payload: response.data } )
11 | })
12 | }
13 |
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/src/components/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Component } from 'react';
3 | import { connect } from 'react-redux'
4 | import { showUsers } from '../actions'
5 | import { Table } from 'react-bootstrap'
6 |
7 | class App extends Component {
8 |
9 | componentWillMount() {
10 | this.props.showUsers()
11 | }
12 |
13 | renderUsersList() {
14 | return this.props.users.map((user) => {
15 | return (
16 |
17 | {user.id} |
18 | {user.name} |
19 | {user.email} |
20 |
21 | )
22 | })
23 | }
24 |
25 | render() {
26 | return (
27 |
28 |
Users List
29 |
30 |
31 |
32 | Id |
33 | Name |
34 | Email |
35 |
36 |
37 |
38 | { this.renderUsersList() }
39 |
40 |
41 |
42 | );
43 | }
44 | }
45 |
46 | function mapStateToProps(state) {
47 | return {
48 | users: state.user.list
49 | }
50 | }
51 |
52 | export default connect(mapStateToProps, { showUsers })(App)
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Provider } from 'react-redux';
4 | import { createStore, applyMiddleware } from 'redux';
5 |
6 | import App from './components/app';
7 | import reducers from './reducers';
8 | import thunk from 'redux-thunk'
9 |
10 |
11 | const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
12 |
13 | ReactDOM.render(
14 |
15 |
16 |
17 | , document.querySelector('.container'));
18 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { showUsers } from './users'
3 |
4 | const rootReducer = combineReducers({
5 | user: showUsers
6 | });
7 |
8 | export default rootReducer;
9 |
--------------------------------------------------------------------------------
/src/reducers/users.js:
--------------------------------------------------------------------------------
1 | import { SHOW_USERS } from '../actions'
2 |
3 | const initialState = {
4 | list: []
5 | }
6 |
7 | export function showUsers(state = initialState, action) {
8 |
9 | switch (action.type) {
10 | case SHOW_USERS:
11 | return Object.assign({}, state, {list: action.payload})
12 | default:
13 | return state
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/style/style.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlmonteagudo/react-redux-api-rest-example/5663ad9d51b98555d7d313a123f3c51c4cf626c2/style/style.css
--------------------------------------------------------------------------------
/test/components/app_test.js:
--------------------------------------------------------------------------------
1 | import { renderComponent , expect } from '../test_helper';
2 | import App from '../../src/components/app';
3 |
4 | describe('App' , () => {
5 | let component;
6 |
7 | beforeEach(() => {
8 | component = renderComponent(App);
9 | });
10 |
11 | it('renders something', () => {
12 | expect(component).to.exist;
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/test/test_helper.js:
--------------------------------------------------------------------------------
1 | import _$ from 'jquery';
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import TestUtils from 'react-addons-test-utils';
5 | import jsdom from 'jsdom';
6 | import chai, { expect } from 'chai';
7 | import chaiJquery from 'chai-jquery';
8 | import { Provider } from 'react-redux';
9 | import { createStore } from 'redux';
10 | import reducers from '../src/reducers';
11 |
12 | global.document = jsdom.jsdom('');
13 | global.window = global.document.defaultView;
14 | const $ = _$(window);
15 |
16 | chaiJquery(chai, chai.util, $);
17 |
18 | function renderComponent(ComponentClass, props = {}, state = {}) {
19 | const componentInstance = TestUtils.renderIntoDocument(
20 |
21 |
22 |
23 | );
24 |
25 | return $(ReactDOM.findDOMNode(componentInstance));
26 | }
27 |
28 | $.fn.simulate = function(eventName, value) {
29 | if (value) {
30 | this.val(value);
31 | }
32 | TestUtils.Simulate[eventName](this[0]);
33 | };
34 |
35 | export {renderComponent, expect};
36 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | entry: [
3 | './src/index.js'
4 | ],
5 | output: {
6 | path: __dirname,
7 | publicPath: '/',
8 | filename: 'bundle.js'
9 | },
10 | module: {
11 | loaders: [{
12 | exclude: /node_modules/,
13 | loader: 'babel'
14 | }]
15 | },
16 | resolve: {
17 | extensions: ['', '.js', '.jsx']
18 | },
19 | devServer: {
20 | historyApiFallback: true,
21 | contentBase: './'
22 | }
23 | };
24 |
--------------------------------------------------------------------------------