I want to get a list of repositories from GitHub's API using redux in reactjs, but I get this error:
./src/components/layouts/page/index.js Line 14: Parsing error:
Unexpected token
function mapDispatchToProps(dispatch) {
^
return {
fetchRepos: function() {
dispatch(fetchRepos());
These are my files:
actions.js
export function fetchRepos() {
return function(dispatch) {
dispatch({
type: 'FETCH_REPOS_REQUEST'
});
return fetch('curl https://api.github.com/search/repositories?q=sort=stars&order=desc')
.then(response => response.json().then(body => ({ response, body })))
.then(({ response, body }) => {
if (!response.ok) {
dispatch({
type: 'FETCH_REPOS_FAILURE',
error: body.error
});
} else {
dispatch({
type: 'FETCH_REPOS_SUCCESS',
repos: body.repos
});
}
}
);
}
}
app.js
import React from 'react';
import './App.css';
import Page from './components/layouts/page/index';
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
function App() {
return (
<Provider store={store}>
<Page />
</Provider>
);
}
export default App;
reducers/index.js
import { combineReducers } from 'redux';
import repos from './reducers';
const rootReducer = combineReducers({
repos,
});
export default rootReducer;
reducers/reducers.js
import { combineReducers } from 'redux';
const INITIAL_STATE = {
items: [],
isFetching: false,
error: undefined
};
function reposReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case 'FETCH_REPOS_REQUEST':
return Object.assign({}, state, {
isFetching: true
});
case 'FETCH_REPOS_SUCCESS':
return Object.assign({}, state, {
isFetching: false,
repos: action.repos
});
case 'FETCH_REPOS_FAILURE':
return Object.assign({}, state, {
isFetching: false,
error: action.error
});
default:
return state;
}
}
export default combineReducers({
repos: reposReducer
});
Page.js
import React, { Component } from 'react';
import List from '../list/index.js';
import { connect } from 'react-redux';
import { fetchRepos } from '../../../actions/actions';
import './page.scss';
class Page extends Component {
componentDidMount() {
this.props.fetchRepos();
}
function mapDispatchToProps(dispatch) {
return {
fetchRepos: function() {
dispatch(fetchRepos());
}
};
}
function mapStateToProps(state) {
return {
repos: state.repos
};
}
render() {
return <List items={this.props.repos}/>
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Page);
Your mapDispatchToProps and mapStateToProps need to be defined outside of your component in order to be used by connect:
class Page extends Component {
componentDidMount() {
this.props.fetchRepos();
}
render() {
const { repos } = this.props;
return <List items={repos} />;
}
}
const mapDispatchToProps = dispatch => ({
fetchRepos: () => dispatch(fetchRepos())
});
const mapStateToProps = state => ({
repos: state.repos
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Page);
For more information refer to this.
Related
i used react 16+ and redux get jsonplaceholder fake data to assign posts state but not working. can't assign the state. how can i assign json values into state using concat method. i check lifecycle methods also but can't get the answer.
Reducer
import * as actiontypes from './actions';
import axios from 'axios';
const initalstate = {
counter: 0,
posts: []
};
const reducer = (state = initalstate, action ) => {
switch (action.type) {
case actiontypes.actionFetchPost:
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(res => {
return {
...state,
posts: state.posts.concat(res.data)
}
});
break;
default :
return state;
}
};
export default reducer;
Redux reducers must be pure functions, it means they should not contain any side effects like calling api.
You need to call api in action creators using redux-thunk package.
Codesandbox
An example action creator:
import {
FETCH_POSTS_STARTED,
FETCH_POSTS_FAILURE,
FETCH_POSTS_SUCCESS
} from "./actionTypes";
import axios from "axios";
export const fetchPosts = () => {
return dispatch => {
dispatch(fetchPostsStarted());
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then(res => {
dispatch(fetchPostsSuccess(res.data));
})
.catch(err => {
dispatch(fetchPostsFailed(err.message));
});
};
};
const fetchPostsStarted = () => {
return {
type: FETCH_POSTS_STARTED,
payload: {
isLoading: true
}
};
};
const fetchPostsSuccess = posts => {
return {
type: FETCH_POSTS_SUCCESS,
payload: {
posts
}
};
};
const fetchPostsFailed = error => {
return {
type: FETCH_POSTS_FAILURE,
payload: {
error
}
};
};
And reducer file:
import {
FETCH_POSTS_STARTED,
FETCH_POSTS_SUCCESS,
FETCH_POSTS_FAILURE
} from "../actions/actionTypes";
const initialState = {
posts: [],
loading: false,
error: null
};
export default function(state = initialState, action) {
switch (action.type) {
case FETCH_POSTS_STARTED:
return {
...state,
loading: true
};
case FETCH_POSTS_SUCCESS:
return {
...state,
loading: false,
error: null,
posts: action.payload.posts
};
case FETCH_POSTS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error
};
default:
return state;
}
}
In store we use redux-thunk like this:
import { createStore, compose, applyMiddleware, combineReducers } from "redux";
import reduxThunk from "redux-thunk";
import postsReducers from "./reducers/postsReducers";
const rootReducer = combineReducers({
posts: postsReducers
});
const store = createStore(rootReducer, compose(applyMiddleware(reduxThunk)));
export default store;
Posts component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchPosts } from "./store/actions/postsActions";
class Posts extends Component {
componentDidMount() {
this.props.fetchPosts();
}
render() {
const { posts, loading, error } = this.props;
return (
<div>
{loading && <div>LOADING...</div>}
{error && <div>{error}</div>}
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
}
const mapStateToProps = state => {
const { posts, loading, error } = state.posts;
return {
posts,
loading,
error
};
};
export default connect(
mapStateToProps,
{
fetchPosts
}
)(Posts);
Index.js
import ReactDOM from "react-dom";
import store from "./store/store";
import { Provider } from "react-redux";
import Posts from "./Posts";
function App() {
return (
<div className="App">
<Posts />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
Do api call in action and return promise, use redux-thunk and redux-promise-middleware:
export const myApiCall = (args1, arg2) => async (dispatch, getState) => {
const payload = fetch({ ...config });
return dispatch({ type: 'MY_API_CALL', payload });
}
Then in reducer will have to handle two results:
MY_API_CALL_FULFILLED and MY_API_CALL_REJECTED
I want to get a response containing repositories from GitHub API and all I get is an empty array, this is my first time working with redux and redux thunk in react, this is my code:
App.js
import React from 'react';
import './App.css';
import Page from './components/layouts/page/index';
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
function App() {
return (
<Provider store={store}>
<Page />
</Provider>
);
}
export default App;
reducers/index.js
import { combineReducers } from 'redux';
import repos from './reducers';
const rootReducer = combineReducers({
repos,
});
export default rootReducer;
reducers/reducers.js
import { combineReducers } from 'redux';
const INITIAL_STATE = {
items: [],
isFetching: false,
error: undefined
};
function reposReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case 'FETCH_REPOS_REQUEST':
return Object.assign({}, state, {
isFetching: true
});
case 'FETCH_REPOS_SUCCESS':
return Object.assign({}, state, {
isFetching: false,
repos: action.repos
});
case 'FETCH_REPOS_FAILURE':
return Object.assign({}, state, {
isFetching: false,
error: action.error
});
default:
return state;
}
}
export default combineReducers({
repos: reposReducer
});
actions/actions.js
export function fetchRepos() {
return function(dispatch) {
dispatch({
type: 'FETCH_REPOS_REQUEST'
});
return fetch('https://api.github.com/search/repositories?q=sort=stars&order=desc')
.then(response => response.json().then(body => ({ response, body})))
.then(({ response, body }) => {
if (!response.ok) {
dispatch({
type: 'FETCH_REPOS_FAILURE',
error: body.error
});
} else {
dispatch({
type: 'FETCH_REPOS_SUCCESS',
repos: body.repos
});
}
}
);
}
}
pages.js
import React, { Component } from 'react';
import List from '../list/index.js';
import './page.scss';
import { connect } from 'react-redux';
import { fetchRepos } from '../../../actions/actions';
class Page extends Component {
componentDidMount() {
this.props.fetchRepos();
}
render() {
console.log(this.props);
return <div className="page"><List items={this.props.repos}/></div>
}
}
function mapDispatchToProps(dispatch) {
return {
fetchRepos: function() {
dispatch(fetchRepos());
}
};
}
function mapStateToProps(state) {
return {
repos: state.repos
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Page);
I want to get the response as an array and pass it as a prop to List component and manipulate it there! please let me know what I should change and what I'm not doing right, thanks for the help!!
The returned json from the server has body.items, you are trying to read it from body.repos.
Change your action to:
export function fetchRepos() {
return function(dispatch) {
dispatch({
type: "FETCH_REPOS_REQUEST",
});
return fetch(
"https://api.github.com/search/repositories?q=sort=stars&order=desc",
)
.then(response => response.json().then(body => ({response, body})))
.then(({response, body}) => {
if (!response.ok) {
dispatch({
type: "FETCH_REPOS_FAILURE",
error: body.error,
});
} else {
dispatch({
type: "FETCH_REPOS_SUCCESS",
repos: body.items,
});
}
});
};
}
I'm trying to use redux with react-native. I have created a fetch post data example app , then I just wanted to use with mapDispatchToProps method. I read the documentation and look at some tutorial, it looks similar.
Problem is when I try to use mapDispatchToProps its return error:
ReferenceError: ReferenceError: Can't find variable: dispatch
An error appears in HomeScreen
My Home Screen
import React from 'react';
import { View } from 'react-native';
import { connect } from 'react-redux';
import { getPosts } from '../actions/index';
class HomeScreen extends React.Component {
state = {
data:[],
}
componentDidMount() {
this.props.getPosts()
}
render() {
return (
<View style={styles.container}>
</View>
);
}
}
function mapStateToProps (state) {
return {
posts: state.posts
};
};
function mapDispatchToProps (dispatch) {
return {
getPosts: () => {
dispatch(getPosts())
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen)
My Store
import { createStore } from 'redux';
import rootReducer from '../reducers';
export default store = createStore(rootReducer);
My Action
import { GET_PGET_POST_LOADING,GET_POST_RECEIVED,GET_POST_ERROR } from './actionTypes';
import { GET_POST_URL } from '../api';
export const getPosts = () => {
dispatch({
type: GET_POST_LOADING
});
fetch(GET_POST_URL).then((data) => {
console.log(data);
dispatch({
type:GET_POST_RECEIVED,
payload:data
})
}).catch((error) => {
console.log(error)
})
}
My Reducer
import { GET_POST_LOADING, GET_POST_RECEIVED, GET_POST_ERROR } from '../actions/actionTypes';
const Post = (state = { posts: [] , loading: true }, action) => {
console.log(action);
switch(action.type) {
case GET_POST_LOADING:
return {
...state,
loading: true
}
case GET_POST_RECEIVED:
return {
loading:false,
posts: action.payload
}
case GET_POST_ERROR:
return state;
default: return state;
}
}
export default Post;
My Appjs
import React from 'react';
import { Platform, StatusBar, StyleSheet, View } from 'react-native';
import { AppLoading, Asset, Font, Icon } from 'expo';
import AppNavigator from './navigation/AppNavigator';
import { Provider } from 'react-redux';
import store from './store/index';
export default class App extends React.Component {
state = {
isLoadingComplete: false,
};
render() {
return (
<Provider store={store}>
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<AppNavigator />
</View>
</Provider>
);
}
}
I'm new about Redux, where I'm missing?
Thanks in advance :)
I'm trying to follow this tutorial (https://medium.com/#stowball/a-dummys-guide-to-redux-and-thunk-in-react-d8904a7005d3) on React Redux but am getting an empty object when I print out this.props. It seems like mapStateToProps isn't actually setting this.props since react redux's connect isn't being called, but I'm not sure why
This is what the state should be (like in the tutorial), all I did was change the component's name ItemList to Home
Here's what I'm getting (nothing was mapped to the state):
actions/items.js
export function itemsHasErrored(bool) {
return {
type: 'ITEMS_HAS_ERRORED',
hasErrored: bool
};
}
export function itemsIsLoading(bool) {
return {
type: 'ITEMS_IS_LOADING',
isLoading: bool
};
}
export function itemsFetchDataSuccess(items) {
return {
type: 'ITEMS_FETCH_DATA_SUCCESS',
items
};
}
export function itemsFetchData(url) {
return (dispatch) => {
dispatch(itemsIsLoading(true));
fetch(url)
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
dispatch(itemsIsLoading(false));
return response;
})
.then((response) => response.json())
.then((items) => dispatch(itemsFetchDataSuccess(items)))
.catch(() => dispatch(itemsHasErrored(true)));
};
}
components/Home.js
export class Home extends Component {
componentDidMount() {
console.log(this.props)
}
render() {
if (this.props.hasErrored) {
return <p>Sorry! There was an error loading the items</p>;
}
if (this.props.isLoading) {
return <p>Loading…</p>;
}
return (
<ul>
{this.props.items.map((item) => (
<li key={item.id}>
{item.label}
</li>
))}
</ul>
);
}
}
const mapStateToProps = (state) => {
return {
items: state.items,
hasErrored: state.itemsHasErrored,
isLoading: state.itemsIsLoading
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: (url) => dispatch(itemsFetchData(url))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
reducers/index.js
export function itemsHasErrored(state = false, action) {
switch (action.type) {
case 'ITEMS_HAS_ERRORED':
return action.hasErrored;
default:
return state;
}
}
export function itemsIsLoading(state = false, action) {
switch (action.type) {
case 'ITEMS_IS_LOADING':
return action.isLoading;
default:
return state;
}
}
export function items(state = [], action) {
switch (action.type) {
case 'ITEMS_FETCH_DATA_SUCCESS':
return action.items;
default:
return state;
}
}
store/configureStore.js
import { applyMiddleware, createStore } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from '../reducers'
export default function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
)
}
App.js
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import configureStore from './store/configureStore'
import { Home } from './components/Home'
const store = configureStore()
export default class App extends Component {
render () {
return (
<Provider store={store}>
<Home />
</Provider>
)
}
}
index.js (I am using create-react-app boilerplate)
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
The issue is that you're exporting two components... the non-connected (via the named export Home with export class Home...) and connected (via export default). Then, you're importing and rendering the non-connected component:
import { Home } from './components/Home'
Since you want to use the connected component, you should be importing the default export like this:
import Home from './components/Home'
You may want to just export the connected component, unless you have some reason to use the unconnected one.
I am building a react app and implementing redux for data. When I navigate to a particular route, I want to dispatch the action to fetch the data from an external API and then once the data comes back, display the data for the user.
Store :
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import { syncHistoryWithStore } from 'react-router-redux';
import { browserHistory } from 'react-router';
import rootReducer from '../reducers/index';
const initialState = {
marvel :{
characters: []
}
};
const store = createStore(rootReducer, initialState, applyMiddleware(thunkMiddleware, createLogger()));
export const history = syncHistoryWithStore(browserHistory, store);
if (module.hot) {
module.hot.accept('../reducers/', () => {
const nextRootReducer = require('../reducers/index').default;
store.replaceReducer(nextRootReducer);
});
}
export default store;
reducer :
import * as constants from '../constants/constants';
const initialState = {
characters: [],
isFetching: false,
errorMessage: null
};
const marvelReducer = (state = initialState, action) => {
switch (action.type) {
case constants.FETCH_MARVEL :
return Object.assign({},state,{isFetching: true});
case constants.FETCH_MARVEL_SUCCESS :
return Object.assign({}. state,{
characters: [...action.response],
isFetching: false
});
case constants.FETCH_MARVEL_ERROR :
return Object.assign({}, state,{
isFetching: false,
errorMessage: action.message
});
default :
return state;
}
};
export default marvelReducer;
actions:
import 'whatwg-fetch';
import * as constants from '../constants/constants';
export const fetchMarvel = (dispatch) => {
const MARVEL_API = 'http://gateway.marvel.com:80/v1/public/characters?apikey=e542b1d89f93ed41b132eda89b9efb2c';
dispatch({
type: constants.FETCH_MARVEL
});
return fetch(MARVEL_API).then(
response => {
dispatch({
type: constants.FETCH_MARVEL_SUCCESS,
response
});
},
error => {
dispatch({
type: constants.FETCH_MARVEL_ERROR,
message: error.message || 'Something went wrong with fetchMarvel'
});
});
};
component :
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actions from '../actions/actions';
import '../styles/homeStyles.css';
class Home extends Component {
render() {
const { characters, isFetching, errorMessage } = this.props;
return (
<div>
<h1>React Starter App</h1>
<h2>This is the home page</h2>
</div>
);
}
}
function mapStateToProps(state) {
return {
characters: state.characters,
isFetching: state.isFetching,
errorMessage: state.errorMessage
};
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actions, dispatch) };
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
I know I'm not currently displaying the props anywhere in the application, but first I am just trying to get them populated.
What step am I missing to dispatch the action so that I can populate the props from state?
You are not dispatching the action anywhere. So nothing happens.
You probably want to do this in a React lifecycle hook, for example:
class Home extends Component {
componentDidMount() {
this.props.actions.fetchMarvel();
}
render() {
const { characters, isFetching, errorMessage } = this.props;
return (
<div>
<h1>React Starter App</h1>
<h2>This is the home page</h2>
</div>
);
}
}