diff --git a/.eslintrc.js b/.eslintrc.js index 03110529d..665ddc46d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -52,5 +52,7 @@ module.exports = { 'react/prop-types': 'off', 'react/react-in-jsx-scope': 'off', + + 'import/no-unresolved': 'off', }, }; diff --git a/__mocks__/react-redux.js b/__mocks__/react-redux.js new file mode 100644 index 000000000..d70cb91bf --- /dev/null +++ b/__mocks__/react-redux.js @@ -0,0 +1,3 @@ +export const useDispatch = jest.fn((dispatch) => dispatch); + +export const useSelector = jest.fn((selector) => selector({})); diff --git a/fixtures/data.js b/fixtures/data.js new file mode 100644 index 000000000..0fd8889ac --- /dev/null +++ b/fixtures/data.js @@ -0,0 +1,4 @@ +export const regions = [{ id: 1, name: '서울' }, { id: 2, name: '부산' }]; +export const categories = [{ id: 1, name: '한식' }, { id: 2, name: '양식' }]; +export const restaurants = [{ id: 1, name: '코코식당' }, { id: 2, name: '네네식당' }]; +export const selectedRegionAndCategory = { selectedRegion: { id: 1, name: '서울' }, selectedCategory: { id: 1, name: '한식' } }; diff --git a/src/App.jsx b/src/App.jsx index 0bd563929..294b81bdd 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,14 @@ +import RegionContainer from './RegionContainer'; +import CategoriesContainer from './CategoriesContainer'; +import RestaurantsContainer from './RestaurantsContainer'; + export default function App() { return ( - <> + <> +

Restaurants

+ + + + ); } diff --git a/src/App.test.jsx b/src/App.test.jsx new file mode 100644 index 000000000..1cea11d56 --- /dev/null +++ b/src/App.test.jsx @@ -0,0 +1,78 @@ +import { fireEvent, render } from '@testing-library/react'; +import { useDispatch, useSelector } from 'react-redux'; +import App from './App'; +import { + regions, categories, restaurants, selectedRegionAndCategory, +} from '../fixtures/data'; + +describe('App', () => { + jest.mock('react-redux'); + jest.mock('./services/api'); + + const dispatch = jest.fn(); + + useDispatch.mockImplementation(() => dispatch); + useSelector.mockImplementation(() => ({ + regions, + categories, + restaurants, + selectedRegionAndCategory, + })); + + const renderApp = () => render(); + + describe('RegionContainer가 렌더링 된다.', () => { + it('지역정보들이 input으로 보인다.', () => { + const { getByText } = renderApp(); + regions.forEach((region) => expect(getByText(new RegExp(`[${region.name}]`))).not.toBeNull()); + }); + + it('input 클릭시 상태가 업데이트된다.', () => { + const { getByText } = renderApp(); + fireEvent.click(getByText(/서울/)); + expect(dispatch).toBeCalled(); + }); + + it('선택된 input은 v가 표시된다.', () => { + const { getByText } = renderApp(); + expect(getByText('서울 v')).not.toBeNull(); + }); + }); + + describe('CategoryContainer가 렌더링 된다.', () => { + it('카테고리 목록이 input으로 보인다.', () => { + const { getByText } = renderApp(); + expect(getByText(/한식/)).not.toBeNull(); + expect(getByText(/양식/)).not.toBeNull(); + }); + + it('카테고리 input 클릭시 상태가 업데이트된다.', () => { + const { getByText } = renderApp(); + fireEvent.click(getByText(/한식/)); + expect(dispatch).toBeCalled(); + }); + + it('선택된 input은 v가 표시된다.', () => { + const { getByText } = renderApp(); + expect(getByText('한식 v')).not.toBeNull(); + }); + }); + + describe('RestaurantContainer가 렌더링 된다.', () => { + it('불러온 레스토랑 데이터들이 보인다.', () => { + const { getByText } = renderApp(); + expect(getByText(/코코식당/)).not.toBeNull(); + expect(getByText(/네네식당/)).not.toBeNull(); + }); + it('선택된 식당이 바뀌면 ', () => { + useSelector.mockImplementation(() => ({ + restaurants: [ + { id: 1, name: '바뀐식당' }, { id: 2, name: '무슨식당' }, + ], + })); + const { getByText } = renderApp(); + expect(getByText('바뀐식당')).not.toBeNull(); + expect(getByText('무슨식당')).not.toBeNull(); + }); + }); +}); diff --git a/src/CategoriesContainer.jsx b/src/CategoriesContainer.jsx new file mode 100644 index 000000000..eec89760e --- /dev/null +++ b/src/CategoriesContainer.jsx @@ -0,0 +1,31 @@ +import { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { loadCategories, setSelectedRegionAndCategory } from './action'; +import Category from './Category'; + +export default function CategoriesContainer() { + const dispatch = useDispatch(); + const { categories, selectedRegionAndCategory } = useSelector((state) => ({ + categories: state.categories, + selectedRegionAndCategory: state.selectedRegionAndCategory, + })); + + useEffect(() => { + dispatch(loadCategories()); + }, []); + + function handleClickCategory(category) { + dispatch(setSelectedRegionAndCategory({ + selectedRegion: selectedRegionAndCategory?.selectedRegion, + selectedCategory: category, + })); + } + + return ( + + ); +} diff --git a/src/CategoriesContainer.test.jsx b/src/CategoriesContainer.test.jsx new file mode 100644 index 000000000..80b728970 --- /dev/null +++ b/src/CategoriesContainer.test.jsx @@ -0,0 +1,45 @@ +import { render, fireEvent } from '@testing-library/react'; +import { useDispatch, useSelector } from 'react-redux'; +import { categories } from '../fixtures/data'; +import { setSelectedRegionAndCategory } from './action'; +import CategoriesContainer from './CategoriesContainer'; + +describe('CategoriesContainer', () => { + const dispatch = jest.fn(); + const renderCategoriesContainer = () => render(); + + jest.mock('react-redux'); + jest.mock('./services/api'); + + useSelector.mockImplementation(() => ({ categories })); + + useDispatch.mockImplementation(() => dispatch); + + it('데이터 받기', () => { + const { getByText } = renderCategoriesContainer(); + expect(getByText(categories[0].name)).not.toBeNull(); + }); + + describe('button 클릭이 되면', () => { + it('클릭 된 버튼의 데이터를 state에 저장한다', () => { + const { getByText } = renderCategoriesContainer(); + // expect(dispatch).not.toBeCalled(); + fireEvent.click(getByText(categories[0].name)); + expect(dispatch).toBeCalledWith(setSelectedRegionAndCategory({ + selectedRegion: undefined, + selectedCategory: categories[0], + })); + }); + }); + + // it('dispatch가 실행된다', () => { + // const category = categories[0]; + // renderCategoriesContainer(category); + + // expect(dispatch).not.toBeCalled(); + + // fireEvent.click(screen.getByText(category.name)); + + // expect(dispatch).toBeCalledWith(selectCategory(category.id)); + // }); +}); diff --git a/src/Category.jsx b/src/Category.jsx new file mode 100644 index 000000000..c0aee57fa --- /dev/null +++ b/src/Category.jsx @@ -0,0 +1,15 @@ +export default function Category({ categories, selectedRegionAndCategory, onClick }) { + return ( + <> + {categories?.map((category) => ( + + ))} + + ); +} diff --git a/src/Category.test.jsx b/src/Category.test.jsx new file mode 100644 index 000000000..9ec4ea989 --- /dev/null +++ b/src/Category.test.jsx @@ -0,0 +1,29 @@ +import { render, fireEvent } from '@testing-library/react'; +import Category from './Category'; +import { categories, selectedRegionAndCategory } from '../fixtures/data'; + +describe('Region', () => { + const onClick = jest.fn(); + + const renderCategory = () => render( + , + ); + + it('지역 input이 보인다.', () => { + const { getByText } = renderCategory(); + expect(getByText('양식')).not.toBeNull(); + }); + it('지역 input을 클릭하면 함수를 호출한다.', () => { + const { getByText } = renderCategory(); + fireEvent.click(getByText('양식')); + expect(onClick).toBeCalledWith({ id: 2, name: '양식' }); + }); + it('선택된 input 옆에는 체크표시가 된다.', () => { + const { getByText } = renderCategory(); + expect(getByText('한식 v')).not.toBeNull(); + }); +}); diff --git a/src/Region.jsx b/src/Region.jsx new file mode 100644 index 000000000..2ed4e44b2 --- /dev/null +++ b/src/Region.jsx @@ -0,0 +1,15 @@ +export default function Region({ regions, selectedRegionAndCategory, onClick }) { + return ( +
+ {regions?.map((item) => ( + + ))} +
+ ); +} diff --git a/src/Region.test.jsx b/src/Region.test.jsx new file mode 100644 index 000000000..fa50d1f8b --- /dev/null +++ b/src/Region.test.jsx @@ -0,0 +1,29 @@ +import { render, fireEvent } from '@testing-library/react'; +import Region from './Region'; +import { regions, selectedRegionAndCategory } from '../fixtures/data'; + +describe('Region', () => { + const onClick = jest.fn(); + + const renderRegion = () => render( + , + ); + + it('지역 input이 보인다.', () => { + const { getByText } = renderRegion(); + expect(getByText('부산')).not.toBeNull(); + }); + it('지역 input을 클릭하면 함수를 호출한다.', () => { + const { getByText } = renderRegion(); + fireEvent.click(getByText('부산')); + expect(onClick).toBeCalledWith({ id: 2, name: '부산' }); + }); + it('선택된 input 옆에는 체크표시가 된다.', () => { + const { getByText } = renderRegion(); + expect(getByText('서울 v')).not.toBeNull(); + }); +}); diff --git a/src/RegionContainer.jsx b/src/RegionContainer.jsx new file mode 100644 index 000000000..866070621 --- /dev/null +++ b/src/RegionContainer.jsx @@ -0,0 +1,32 @@ +import { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { setSelectedRegionAndCategory, loadRegions } from './action'; +import Region from './Region'; + +export default function RegionContainer() { + // 데이터 페칭하기 + const dispatch = useDispatch(); + const { regions, selectedRegionAndCategory } = useSelector((state) => ({ + regions: state.regions, + selectedRegionAndCategory: state.selectedRegionAndCategory, + })); + + useEffect(() => { + dispatch(loadRegions()); + }, []); + + function handleClickButton(item) { + dispatch(setSelectedRegionAndCategory({ + selectedCategory: selectedRegionAndCategory?.selectedCategory, + selectedRegion: item, + })); + } + + return ( + + ); +} diff --git a/src/RegionContainer.test.jsx b/src/RegionContainer.test.jsx new file mode 100644 index 000000000..48dcebecd --- /dev/null +++ b/src/RegionContainer.test.jsx @@ -0,0 +1,22 @@ +import { fireEvent, render } from '@testing-library/react'; +import { useDispatch, useSelector } from 'react-redux'; +import { regions } from '../fixtures/data'; +import RegionContainer from './RegionContainer'; + +describe('RegionContainer', () => { + const dispatch = jest.fn(); + jest.mock('react-redux'); + jest.mock('./services/api'); + useDispatch.mockImplementation(() => dispatch); + useSelector.mockImplementation(() => ({ regions })); + + const renderRegionContainer = () => render(); + + it('데이터 받기', () => { + const { getByText } = renderRegionContainer(); + + expect(getByText('서울')).not.toBeNull(); + fireEvent.click(getByText('서울')); + expect(dispatch).toBeCalled(); + }); +}); diff --git a/src/Restaurants.jsx b/src/Restaurants.jsx new file mode 100644 index 000000000..2a6413713 --- /dev/null +++ b/src/Restaurants.jsx @@ -0,0 +1,7 @@ +export default function Restaurants({ restaurants }) { + return ( + <> + {restaurants?.map((restaurant) =>
  • {restaurant.name}
  • )} + + ); +} diff --git a/src/Restaurants.test.jsx b/src/Restaurants.test.jsx new file mode 100644 index 000000000..400e62d20 --- /dev/null +++ b/src/Restaurants.test.jsx @@ -0,0 +1,13 @@ +import { render } from '@testing-library/react'; +import Restaurants from './Restaurants'; +import { restaurants } from '../fixtures/data'; + +describe('RestaurantsContainer', () => { + const renderRestaurants = () => render(); + + it('식당 리스트가 보인다', () => { + const { getByText } = renderRestaurants(); + expect(getByText('코코식당')).not.toBeNull(); + expect(getByText('네네식당')).not.toBeNull(); + }); +}); diff --git a/src/RestaurantsContainer.jsx b/src/RestaurantsContainer.jsx new file mode 100644 index 000000000..9f89ff555 --- /dev/null +++ b/src/RestaurantsContainer.jsx @@ -0,0 +1,29 @@ +import { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { loadRestaurants } from './action'; +import Restaurants from './Restaurants'; + +export default function RestaurantsContainer() { + const { restaurants, selectedRegion, selectedCategory } = useSelector((state) => ({ + restaurants: state.restaurants, + selectedRegion: state.selectedRegionAndCategory.selectedRegion, + selectedCategory: state.selectedRegionAndCategory.selectedCategory, + })); + + const dispatch = useDispatch(); + + useEffect(() => { + const isEmptyObject = (object) => Object.keys(object || {}).length === 0; + + if (isEmptyObject(selectedRegion) || isEmptyObject(selectedCategory)) { + return; + } + dispatch(loadRestaurants(selectedRegion, selectedCategory)); + }, [selectedRegion, selectedCategory]); + + return ( +
      + +
    + ); +} diff --git a/src/RestaurantsContainer.test.jsx b/src/RestaurantsContainer.test.jsx new file mode 100644 index 000000000..d4f2952e2 --- /dev/null +++ b/src/RestaurantsContainer.test.jsx @@ -0,0 +1,29 @@ +import { render } from '@testing-library/react'; +import { useDispatch, useSelector } from 'react-redux'; +import { restaurants } from '../fixtures/data'; +import RestaurantsContainer from './RestaurantsContainer'; + +describe('RestaurantsContainer', () => { + const dispatch = jest.fn(); + jest.mock('react-redux'); + jest.mock('./services/api'); + + useDispatch.mockImplementation(() => dispatch); + useSelector.mockImplementation(() => ({ restaurants })); + + const renderRestaurantsContainer = () => render(); + + it('식당 리스트가 보인다', () => { + const { getByText } = renderRestaurantsContainer(); + expect(getByText('코코식당')).not.toBeNull(); + }); + it('받아온 식당데이터가 변경되는 경우 식당이 보인다. ', () => { + useSelector.mockImplementation(() => ({ + restaurants: [ + { id: 1, name: '바뀐식당' }, { id: 2, name: '이렇게' }, + ], + })); + const { getByText } = renderRestaurantsContainer(); + expect(getByText('바뀐식당')).not.toBeNull(); + }); +}); diff --git a/src/action.js b/src/action.js new file mode 100644 index 000000000..ae3c5d90c --- /dev/null +++ b/src/action.js @@ -0,0 +1,57 @@ +import { fetchRegions, fetchCategories, fetchRestaurants } from './services/api'; + +export function setRegions(regions) { + return { + type: 'SET_REGIONS', + payload: regions, + }; +} + +export function loadRegions() { + return (async (dispatch) => { + const regions = await fetchRegions(); + await dispatch(setRegions(regions)); + }); +} + +export function setCategories(categories) { + return { + type: 'SET_CATEGORIES', + payload: categories, + }; +} + +export function loadCategories() { + return ( + async (dispatch) => { + const categories = await fetchCategories(); + await dispatch(setCategories(categories)); + } + ); +} + +export function setRestaurants(restaurants) { + return { + type: 'SET_RESTAURANTS', + payload: restaurants, + }; +} + +export function setSelectedRegionAndCategory(selectedRegionAndCategory) { + return { + type: 'SET_SELECTED_REGION_AND_CATEGORY', + payload: { selectedRegionAndCategory }, + }; +} + +export function loadRestaurants(selectedRegion = '', selectedCategory = '') { + return ( + async (dispatch) => { + const restaurants = await fetchRestaurants( + selectedRegion.name, + selectedCategory.id, + ); + await dispatch(setRestaurants(restaurants)); + } + ); +} diff --git a/src/action.test.js b/src/action.test.js new file mode 100644 index 000000000..22ae1b139 --- /dev/null +++ b/src/action.test.js @@ -0,0 +1,54 @@ +import { + loadCategories, + loadRegions, + loadRestaurants, +} from './action'; + +import { categories, regions, restaurants } from '../fixtures/data'; + +jest.mock('./services/api'); + +describe('actions', () => { + const dispatch = jest.fn(); + + describe('loadCategories', () => { + beforeEach(() => { + global.fetch = jest.fn(() => Promise.resolve({ + json: () => Promise.resolve(categories), + })); + }); + + it('setCategories를 호출한다 ', async () => { + await loadCategories()(dispatch); + expect(dispatch).toBeCalled(); + }); + }); + + describe('loadRegions', () => { + beforeEach(() => { + global.fetch = jest.fn(() => Promise.resolve({ + json: () => Promise.resolve(regions), + })); + }); + + it('setRegions을 호출한다 ', async () => { + await loadRegions()(dispatch); + + expect(dispatch).toBeCalled(); + }); + }); + + describe('loadRestaurants', () => { + beforeEach(() => { + global.fetch = jest.fn(() => Promise.resolve({ + json: () => Promise.resolve(restaurants), + })); + }); + + it('setRestaurants을 호출한다 ', async () => { + await loadRestaurants()(dispatch); + + expect(dispatch).toBeCalled(); + }); + }); +}); diff --git a/src/index.jsx b/src/index.jsx index 5752f9375..fde9ee3e7 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -1,8 +1,11 @@ import ReactDOM from 'react-dom'; - +import { Provider } from 'react-redux'; import App from './App'; +import store from './store'; ReactDOM.render( - , + + + , document.getElementById('app'), ); diff --git a/src/reducer.js b/src/reducer.js new file mode 100644 index 000000000..d9e4e6b33 --- /dev/null +++ b/src/reducer.js @@ -0,0 +1,50 @@ +const initialState = { + regions: [], + categories: [], + restaurants: [], + selectedRegionAndCategory: { selectedRegion: {}, selectedCategory: {} }, +}; + +const handleAction = { + SET_REGIONS: (state, action) => { + const { regions } = action.payload; + return ({ + ...state, + regions, + }); + }, + SET_CATEGORIES: (state, action) => { + const { categories } = action.payload; + return ({ + ...state, + categories, + }); + }, + + SET_RESTAURANTS: (state, action) => { + const { restaurants } = action.payload; + return ({ + ...state, + restaurants, + }); + }, + + SET_SELECTED_REGION_AND_CATEGORY: (state, action) => { + const { selectedRegion, selectedCategory } = action.payload.selectedRegionAndCategory; + return ({ + ...state, + selectedRegionAndCategory: { + ...state.selectedRegionAndCategory, + selectedRegion, + selectedCategory, + }, + }); + }, +}; + +export default function reducer(state = initialState, action) { + if (handleAction[action.type]) { + return handleAction[action.type](state, action); + } + return state; +} diff --git a/src/reducer.test.js b/src/reducer.test.js new file mode 100644 index 000000000..59f989649 --- /dev/null +++ b/src/reducer.test.js @@ -0,0 +1,44 @@ +import reducer from './reducer'; +import { + setRegions, setCategories, setRestaurants, setSelectedRegionAndCategory, +} from './action'; +import { regions, categories, restaurants } from '../fixtures/data'; + +describe('reducer', () => { + const prevState = { + regions: [], + categories: [], + restaurants: [], + selectedRegionAndCategory: { selectedRegion: {}, selectedCategory: {} }, + + }; + describe('fetch된 Region 데이터가 변경되었을 때', () => { + it('변경된 region state가 반환된다.', () => { + const state = reducer(prevState, setRegions({ regions })); + expect(state.regions[0].name).toBe('서울'); + }); + }); + describe('fetch된 Category 데이터가 변경되었을 때', () => { + it('변경된 region state가 반환된다.', () => { + const state = reducer(prevState, setCategories({ categories })); + expect(state.categories[0].name).toBe('한식'); + }); + }); + describe('fetch된 Restaurant 데이터가 변경되었을 때', () => { + it('변경된 region state가 반환된다.', () => { + const state = reducer(prevState, setRestaurants({ restaurants })); + expect(state.restaurants[0].name).toBe('코코식당'); + }); + }); + describe('클릭한 region과 category가 변경되었을 때 ', () => { + it('변경된 region state가 반환된다.', () => { + const state = reducer(prevState, setSelectedRegionAndCategory({ + selectedRegion: { id: 1, name: '서울' }, + selectedCategory: { id: 1, name: '한식' }, + })); + + expect(state.selectedRegionAndCategory.selectedRegion.name).toBe('서울'); + expect(state.selectedRegionAndCategory.selectedCategory.name).toBe('한식'); + }); + }); +}); diff --git a/src/services/__mock__/api.js b/src/services/__mock__/api.js new file mode 100644 index 000000000..9ad47a808 --- /dev/null +++ b/src/services/__mock__/api.js @@ -0,0 +1,11 @@ +export async function fetchRegions() { + return []; +} + +export async function fetchCategories() { + return []; +} + +export async function fetchRestaurants() { + return []; +} diff --git a/src/services/api.js b/src/services/api.js new file mode 100644 index 000000000..ec51d39f6 --- /dev/null +++ b/src/services/api.js @@ -0,0 +1,20 @@ +export async function fetchRegions() { + const url = 'https://eatgo-customer-api.ahastudio.com/regions'; + const data = await fetch(url); + const result = { regions: await data.json() }; + return result; +} + +export async function fetchCategories() { + const url = 'https://eatgo-customer-api.ahastudio.com/categories'; + const data = await fetch(url); + const result = { categories: await data.json() }; + return result; +} + +export async function fetchRestaurants(regionName, categoryId) { + const url = `https://eatgo-customer-api.ahastudio.com/restaurants?region=${regionName}&category=${categoryId}`; + const data = await fetch(url); + const result = { restaurants: await data.json() }; + return result; +} diff --git a/src/services/api.test.js b/src/services/api.test.js new file mode 100644 index 000000000..b0852da8b --- /dev/null +++ b/src/services/api.test.js @@ -0,0 +1,74 @@ +import { fetchCategories, fetchRegions, fetchRestaurants } from './api'; + +import { regions, categories, restaurants } from '../../fixtures/data'; + +describe('api', () => { + describe('fetchRegions', () => { + beforeEach(() => { + global.fetch = jest.fn(() => Promise.resolve({ + json: () => Promise.resolve(regions), + })); + }); + + const regionUrl = 'https://eatgo-customer-api.ahastudio.com/regions'; + + it('지역 url을 가져온다', async () => { + await fetchRegions(); + + expect(fetch).toHaveBeenCalledWith(regionUrl); + }); + + it('지역 목록을 가져온다', async () => { + const regionsResult = await fetchRegions(); + + expect(regionsResult).toEqual({ regions }); + }); + }); + + describe('fetchCategories', () => { + beforeEach(() => { + global.fetch = jest.fn(() => Promise.resolve({ + json: () => Promise.resolve(categories), + })); + }); + + const categoriesUrl = 'https://eatgo-customer-api.ahastudio.com/categories'; + + it('카테고리 url을 가져온다', async () => { + await fetchCategories(); + + expect(fetch).toHaveBeenCalledWith(categoriesUrl); + }); + + it('카테고리 목록을 가져온다', async () => { + const categoriesResult = await fetchCategories(); + + expect(categoriesResult).toEqual({ categories }); + }); + }); + + describe('fetchRestaurants', () => { + beforeEach(() => { + global.fetch = jest.fn(() => Promise.resolve({ + json: () => Promise.resolve(restaurants), + })); + }); + + const region = regions[0]; + const category = categories[0]; + + const restaurantsUrl = `https://eatgo-customer-api.ahastudio.com/restaurants?region=${region.name}&category=${category.id}`; + + it('레스토랑 url을 가져온다', async () => { + await fetchRestaurants(region.name, category.id); + + expect(fetch).toHaveBeenCalledWith(restaurantsUrl); + }); + + it('레스토랑 목록을 가져온다', async () => { + const restaurantsResult = await fetchRestaurants({ region, category }); + + expect(restaurantsResult).toEqual({ restaurants }); + }); + }); +}); diff --git a/src/store.js b/src/store.js new file mode 100644 index 000000000..7a6984f8f --- /dev/null +++ b/src/store.js @@ -0,0 +1,7 @@ +import { createStore, applyMiddleware } from 'redux'; +import thunk from 'redux-thunk'; +import reducer from './reducer'; + +const store = createStore(reducer, applyMiddleware(thunk)); + +export default store;