;
diff --git a/src/components/pokemon-view.test.js b/src/components/pokemon-view.test.js
deleted file mode 100644
index f269e44..0000000
--- a/src/components/pokemon-view.test.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import React from 'react';
-import { expect, assert } from 'chai';
-import { shallow } from 'enzyme';
-import {PokemonView} from './pokemon-view';
-import sinon from 'sinon';
-
-
-describe('', () => {
-
- it('Should display the name in an H2', () => {
-
- const wrapper = shallow();
-
- expect(wrapper.find('h2')).to.have.length(1);
- expect(wrapper.find('h2').text()).to.contain('Bob');
-
- });
-
- it('should favourite a pokemon on button click', () => {
-
- let mySpy = sinon.spy();
-
- const wrapper = shallow();
-
-
- expect(mySpy.calledOnce).to.equal(false);
- wrapper.find('button').simulate('click');
- expect(mySpy.calledOnce).to.equal(true);
-
- assert(mySpy.calledWith('Jon'), 'I expect the pokemon name that is passed in to be what is called when the button is clicked');
-
- });
-
-});
diff --git a/src/constants/api-url.js b/src/constants/api-url.js
index 0dea808..877c9f6 100644
--- a/src/constants/api-url.js
+++ b/src/constants/api-url.js
@@ -1,3 +1 @@
-const POKEAPI_BASE_URL = 'https://pokeapi.co/api/v2/';
-
-export const GET_ALL_POKEMON_URL = POKEAPI_BASE_URL + 'pokemon';
\ No newline at end of file
+export const POKEAPI_BASE_URL = 'https://pokeapi.co/api/v2';
diff --git a/src/features/app/app.actions.js b/src/features/app/app.actions.js
new file mode 100644
index 0000000..6236e14
--- /dev/null
+++ b/src/features/app/app.actions.js
@@ -0,0 +1,36 @@
+// Remember, thunks are just functions that return another function.
+// It is not super obvious that fetchPokemons is a thunk, since it looks like it's just a function
+// calling another function. However, pokemonRequest actually returns a function, see why in the expansions below.
+// (2 & 3 are not part of the solution)
+
+import { pokemonRequest } from '../../utils/pokemonRequest';
+import { FETCH_POKEMONS } from '../pokemon-list/pokemon-list.types';
+
+// Solution:
+export const fetchPokemons = () =>
+ pokemonRequest({ type: FETCH_POKEMONS, endpoint: 'pokemon' });
+
+// Solution 2. Adds return statement to see that does have a return value.
+export const fetchPokemons2 = () => {
+ return pokemonRequest({ type: FETCH_POKEMONS, endpoint: 'pokemon' });
+};
+
+// Solution 3. Pastes values and pokemonRequest function to see how its a thunk.
+export const fetchPokemons3 = () => {
+ return dispatch => {
+ dispatch({ type: FETCH_POKEMONS.START });
+ return fetch('https://pokeapi.co/api/v2/pokemon')
+ .then(res => res.json())
+ .then(
+ data => {
+ dispatch({ type: FETCH_POKEMONS.SUCCESS, payload: data });
+ },
+ error => {
+ dispatch({ type: FETCH_POKEMONS.FAILURE });
+ },
+ )
+ .catch(eror => {
+ dispatch({ type: FETCH_POKEMONS.FAILURE });
+ });
+ };
+};
diff --git a/src/features/app/app.component.js b/src/features/app/app.component.js
new file mode 100644
index 0000000..abfbf57
--- /dev/null
+++ b/src/features/app/app.component.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import { PokemonCard } from '../pokemon-card/pokemon-card.component';
+import { PokemonListContainer } from '../pokemon-list/pokemon-list.container';
+
+export const App = ({ hasFavouritePokemon, favouritePokemon }) => (
+
+
Pokemon App!
+ {hasFavouritePokemon && }
+
+
+);
diff --git a/src/features/app/app.container.js b/src/features/app/app.container.js
new file mode 100644
index 0000000..b4d83de
--- /dev/null
+++ b/src/features/app/app.container.js
@@ -0,0 +1,16 @@
+import { connect } from 'react-redux';
+import { App } from './app.component';
+import { isNil } from 'ramda';
+
+// Check out ramda for some awesome utily functions!
+// We could have also used "double bang" (!!) to get a boolean value
+// (I try to avoid "truthy" and "falsy" values)
+
+const mapStateToProps = ({ pokemon }) => ({
+ hasFavouritePokemon: !isNil(pokemon.favouritePokemon),
+ favouritePokemon: pokemon.favouritePokemon,
+});
+
+// Notice how my "connected" or "container" components don't have any markup, this is a best practice.
+
+export const AppContainer = connect(mapStateToProps)(App);
diff --git a/src/features/pokemon-card/pokemon-card.component.js b/src/features/pokemon-card/pokemon-card.component.js
new file mode 100644
index 0000000..0b4f593
--- /dev/null
+++ b/src/features/pokemon-card/pokemon-card.component.js
@@ -0,0 +1,12 @@
+import React from 'react';
+
+export const PokemonCard = ({ pokemonName = 'Abdella', height = '183 cm' }) => (
+
+
{pokemonName}
+
+
Height: {height}
+
+);
diff --git a/src/features/pokemon-list/pokemon-list.actions.js b/src/features/pokemon-list/pokemon-list.actions.js
new file mode 100644
index 0000000..b33f967
--- /dev/null
+++ b/src/features/pokemon-list/pokemon-list.actions.js
@@ -0,0 +1,10 @@
+import { UPDATE_FAVOURITE_POKEMON } from './pokemon-list.types';
+
+export function createUpdateFavouritePokemonAction(pokemonName) {
+ return {
+ type: UPDATE_FAVOURITE_POKEMON,
+ payload: {
+ pokemonName,
+ },
+ };
+}
diff --git a/src/features/pokemon-list/pokemon-list.container.js b/src/features/pokemon-list/pokemon-list.container.js
new file mode 100644
index 0000000..e6eacf2
--- /dev/null
+++ b/src/features/pokemon-list/pokemon-list.container.js
@@ -0,0 +1,20 @@
+import { ClickableList } from '../../components/clickable-list';
+import { connect } from 'react-redux';
+import { createUpdateFavouritePokemonAction } from './pokemon-list.actions';
+
+const mapStateToProps = state => ({
+ data: state.pokemon.list.map(item => ({
+ title: item.name,
+ })),
+});
+
+const mapDispatchToProps = dispatch => ({
+ onClickItem: item => dispatch(createUpdateFavouritePokemonAction(item.title)),
+});
+
+// Notice how my "connected" or "container" components don't have any markup, this is a best practice.
+
+export const PokemonListContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(ClickableList);
diff --git a/src/features/pokemon-list/pokemon-list.reducer.js b/src/features/pokemon-list/pokemon-list.reducer.js
new file mode 100644
index 0000000..f3c288b
--- /dev/null
+++ b/src/features/pokemon-list/pokemon-list.reducer.js
@@ -0,0 +1,21 @@
+import { UPDATE_FAVOURITE_POKEMON, FETCH_POKEMONS } from './pokemon-list.types';
+
+// Some features need their own reducer, like this one!
+
+const INITIAL_STATE = {
+ list: [],
+ favouritePokemon: null,
+};
+
+export const pokemonReducer = (state = INITIAL_STATE, { type, payload }) => {
+ switch (type) {
+ case UPDATE_FAVOURITE_POKEMON:
+ return { ...state, ...{ favouritePokemon: payload.pokemonName } };
+
+ case FETCH_POKEMONS.SUCCESS:
+ return { ...state, ...{ list: payload.results } };
+
+ default:
+ return state;
+ }
+};
diff --git a/src/features/pokemon-list/pokemon-list.types.js b/src/features/pokemon-list/pokemon-list.types.js
new file mode 100644
index 0000000..8f05afc
--- /dev/null
+++ b/src/features/pokemon-list/pokemon-list.types.js
@@ -0,0 +1,11 @@
+export const UPDATE_FAVOURITE_POKEMON = 'FAVOURITE_POKEMON';
+
+// FETCH_POKEMONS is an example of a pattern you might see with async redux types.
+// It's not a rule, but when used with pokemonRequest, it can be easy to make hook
+// up redux to our backend api's.
+
+export const FETCH_POKEMONS = {
+ START: 'FETCH_POKEMONS_START',
+ SUCCESS: 'FETCH_POKEMONS_SUCCESS',
+ FAILURE: 'FETCH_POKEMONS_FAILURE',
+};
diff --git a/src/index.js b/src/index.js
index 879c7de..50f17b1 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,20 +1,29 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import { createStore, applyMiddleware } from 'redux';
+import { AppContainer } from './features/app/app.container';
+import { fetchPokemons } from './features/app/app.actions';
+import { createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
-
-import App from './App';
+import { rootReducer } from './reducers';
import './index.css';
-import reducer from './reducers';
-const store = createStore(reducer,
- applyMiddleware(thunk), // this is how thunk is integrated into the redux library
- window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
+const store = createStore(
+ rootReducer,
+ compose(
+ applyMiddleware(thunk),
+ window.__REDUX_DEVTOOLS_EXTENSION__ &&
+ window.__REDUX_DEVTOOLS_EXTENSION__(),
+ ),
+);
ReactDOM.render(
-
-
+
+ ,
- document.getElementById('root')
+ document.getElementById('root'),
);
+
+// You can also use the component did mount hook to make this ajax call in the App component.
+// But since this also only runs once, I figured it was easier to fire it here.
+store.dispatch(fetchPokemons());
diff --git a/src/reducers.js b/src/reducers.js
new file mode 100644
index 0000000..2d30d34
--- /dev/null
+++ b/src/reducers.js
@@ -0,0 +1,6 @@
+import { combineReducers } from 'redux';
+import { pokemonReducer } from './features/pokemon-list/pokemon-list.reducer';
+
+export const rootReducer = combineReducers({
+ pokemon: pokemonReducer,
+});
diff --git a/src/reducers/index.js b/src/reducers/index.js
deleted file mode 100644
index a4f7478..0000000
--- a/src/reducers/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import { combineReducers } from 'redux';
-import {pokemon} from "./pokemon";
-
-
-export default combineReducers({
- pokemon,
-});
diff --git a/src/reducers/pokemon.js b/src/reducers/pokemon.js
deleted file mode 100644
index dd4da40..0000000
--- a/src/reducers/pokemon.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import {ACTION_TYPES} from "../actions/index";
-
-
-const INITIAL_STATE = {
- list: [],
- favouritePokemon: null
-};
-
-export const pokemon = (state = INITIAL_STATE, {type, payload}) => {
-
- switch (type) {
- case ACTION_TYPES.favouritePokemon:
- return {...state, ...{favouritePokemon: payload.pokemon}};
-
- case ACTION_TYPES.setPokemon:
-
- return {...state, ...{list: payload.pokemon}};
-
- default:
- return state;
- }
-
-};
\ No newline at end of file
diff --git a/src/utils/pokemonRequest.js b/src/utils/pokemonRequest.js
new file mode 100644
index 0000000..5620b61
--- /dev/null
+++ b/src/utils/pokemonRequest.js
@@ -0,0 +1,27 @@
+import { POKEAPI_BASE_URL } from '../constants/api-url';
+
+// This is a thunk! It's a function that returns another function!
+// Here it makes an ajax request, and dispatches the different states
+// of the request (ie START, SUCCESS and FAILURE)
+
+export const pokemonRequest = ({ type, endpoint }) => {
+ return dispatch => {
+ dispatch({ type: type.START });
+ return fetch(`${POKEAPI_BASE_URL}/${endpoint}`)
+ .then(res => res.json())
+ .then(
+ data => {
+ dispatch({ type: type.SUCCESS, payload: data });
+ },
+ error => {
+ dispatch({ type: type.FAILURE });
+ },
+ )
+ .catch(eror => {
+ dispatch({ type: type.FAILURE });
+ });
+ };
+};
+
+// We could have abstracted this function out even more so that it could support
+// multiple api's, ajax methods (PUT, POST, etc), and a request body.