I learning redux by following some tutorial. Here I am getting an error
Object(...) is not a function
in this line
export default connect(null, { fetchPosts } )(Posts);
of the following container
import React, { Component } from 'react';
import { connect } from 'react'
import axios from 'axios';
import { fetchPosts } from '../actions/postActions.js';
class Posts extends Component {
componentWillMount() {
this.props.fetchPosts();
}
render() {
const postItems = this.state.posts.map(el => {
return (
<div key={el.id}>
<h3>{el.title} </h3>
<p>{el.body} </p>
</div>
)
})
return (
<div>
<h1> Posts </h1>
{postItems}
</div>
);
}
}
export default connect(null, { fetchPosts } )(Posts);
Ps: I know it will also throw the map error but I am not worried about it at the moment.
Since it says Object(...) is not a function and marks the line export, the only object we have inside export is the fetchPosts which looks like this (it is an action function)
import { FETCH_POST, NEW_POST } from './type.js'
import axios from 'axios';
export const fetchPosts = () => {
return function (dispatch) {
axios.get("https://jsonplaceholder.typicode.com/posts").then((response) => dispatch({
type: FETCH_POST,
payload: response
}))
}
}
Since I am not sure the relevant code for the same, the fetchPosts dispatches an acton to the following reducer
import { FETCH_POST, NEW_POST } from '../actions/type.js'
const initialState = {
items: [],
item: {}
}
export default function(state = initialState, action) {
switch (action.type) {
case FETCH_POST:
return {
...state,
items: action.payload
}
default:
return state
}
}
Which is later combined using combineReducers in our rootreducer.js file (which is then imported to store.js where we create store)
Can Anyone tell me what I am doing wrong and How can we fix it?
React does not have a named connect export, it belongs to react-redux. So, you should import it like that:
import { connect } from "react-redux";
Related
I have just gone through some redux tutorial and started to implement
i have one API call that has to be happen from redux as soon as page loads.. its possible with ComponentDidMount ,but i need to know how redux helps in achieving this.
For easiness i had shared the code in
https://codesandbox.io/s/quirky-sunset-s95gu?fontsize=14
My index.js look like
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { createStore,applyMiddleware } from "redux";
import allReducer from "./reducers";
import { Provider } from "react-redux";
import thunk from 'redux-thunk';
import "./styles.css";
let store = createStore(
allReducer,
applyMiddleware(thunk)
);
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
My Action creater look like(action/index.js)
import UserApi from "../UserApi";
export function loadUserSuccess(user) {
return { type: "LOAD_USER_SUCCESS", user };
}
export function loadUsers() {
return function(dispatch) {
return UserApi.getAllUsers()
.then(user => {
console.log("midhun");
dispatch(loadUserSuccess(user));
})
.catch(error => {
throw error;
});
};
}
and its subsequent api caliing function look like
class UserApi {
static getAllUsers() {
return fetch("https://jsonplaceholder.typicode.com/users")
.then(response => {
console.log("response", response);
return response.json();
})
.catch(error => {
return error;
});
}
}
export default UserApi;
My Reducer look like
import initialState from "./InitialState";
export default function IsLoggedReducer(state = initialState.user, action) {
console.log(state, action);
switch (action.type) {
case "LOAD_USER_SUCCESS":
return state;
default:
return state;
}
}
and my App.js look like
import React from "react";
import { connect } from "react-redux";
import * as userActions from "./action/index";
import UserList from "./UserList";
class App extends React.Component {
render() {
return (
<div>
<h1>MYpage</h1>
<UserList user={this.props.user} />
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {
user: state.user
};
}
export default connect(mapStateToProps)(App);
I had put couple of console in action creator and its subsequent function,but its not triggering.
Any help will be much much apreciated and will be highly help for beginners
You guys can check the complete set of code
https://codesandbox.io/s/quirky-sunset-s95gu?fontsize=14
In addition to the info lavor gaved, since you use combineReducers, you need to access to state by using your reducer key.
App.js
import React from "react";
import { connect } from "react-redux";
import {loadUsers} from "./action/index";
import UserList from "./UserList";
class App extends React.Component {
componentDidMount() {
this.props.loadUsers();
}
render() {
return (
<div>
<h1>MYpage</h1>
<UserList users={this.props.users} />
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {
users: state.IsLoggedReducer
};
}
export default connect(mapStateToProps, {loadUsers})(App);
I also made some corrections in the reducer file, we need to return the new state with the given payload.
import initialState from "./InitialState";
export default function IsLoggedReducer(state = initialState.user, action) {
console.log("ap", action.payload);
switch (action.type) {
case "LOAD_USER_SUCCESS":
return [...action.payload]
default:
return state;
}
}
And action file:
import UserApi from "../UserApi";
export function loadUserSuccess(users) {
return { type: "LOAD_USER_SUCCESS", payload: users };
}
export function loadUsers() {
return function(dispatch) {
return UserApi.getAllUsers()
.then(users => {
dispatch(loadUserSuccess(users));
})
.catch(error => {
throw error;
});
};
}
You can check this codesandbox for the working app.
https://codesandbox.io/s/cranky-colden-v6r6w
You are not dispatching your action, try to do it in componentDidMount (you need to map dispatch to props first):
App.js
componentDidMount() {
this.props.loadUsers();
}
// ...
function mapStateToProps(state, ownProps) {
return {
user: state.user
};
}
function mapDispatchToProps(dispatch) {
return {
loadUsers: () => dispatch(userActions.loadUsers())
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
I'm new to React/Redux. I'm making an app using an API but the code doesn't work. When I run the code it says "this.props.recipes.map is not a function" and doesn't render anything.
If I change payload to: "payload: response.data.recipes" then the error changes to "Given action "FETCH_RECIPE", reducer "recipes" returned undefined." but no errors on screen (only in console). I thought writing "(state = [], action)" would solve the problem but it seems not. What's the problem and how do I fix this error?
Action Creator
import recipe from '../apis/recipe';
export const fetchRecipe = () => async dispatch => {
const response = await recipe.get('');
dispatch({ type: 'FETCH_RECIPE', payload: response.data })
};
Reducer
import { combineReducers } from 'redux';
const recipeReducer = (state = [], action) => {
switch(action.type) {
case 'FETCH_RECIPE':
return action.payload;
default:
return state;
}
};
export default combineReducers({
recipes: recipeReducer
});
import React from 'react';
import { connect } from 'react-redux';
import { fetchRecipe } from '../actions';
class Recipe extends React.Component {
componentDidMount() {
this.props.fetchRecipe();
console.log("This doesn't work", this.props.recipes)
}
renderList() {
return this.props.recipes.map(recipe => {
return (
<div>
<p>{recipe.publisher}</p>
</div>
)
})
}
render() {
console.log("First loaded: empty, second time: data fetched", this.props.recipes)
return (
<div>
{this.renderList()}
</div>
);
}
}
const mapStateToProps = (state) => {
return { recipes: state.recipes }
};
export default connect(mapStateToProps,{
fetchRecipe
})(Recipe);
API Request
import axios from 'axios';
import { key } from './config';
export default axios.create({
baseURL: `https://cors-anywhere.herokuapp.com/https://www.food2fork.com/api/search?key=${key}&q=pizza`
});
I am learning react-redux, so I decided to implement what I have been learning. But I am have a bug challenge. So I console.logged this.props.users from mapStateToProps function.
I believe there's something I not doing right which I don't understand. Please an explanation in other to move on. Thanks you so much for helping out.
Here is my code.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchUsers } from '../actions/userAction';
import UserList from '../components/UserList';
class UserPage extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.fetchUsers();
}
componentDidMount() {
console.log(this.props.users);
}
render() {
return (
<div>
<h2>Users Page</h2>
<UserList users={this.props.users} />
</div>
);
}
}
const mapStateToProps = state => {
return {
users: state.userReducer.users
};
};
const mapDispatchToProps = dispatch => {
return {
fetchUsers: () => dispatch(fetchUsers())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserPage);
So this is what I get from the chrome console - Empty arrays.
props showing empty arrays
But when I check the React DevTool and Redux DevTool, they display the expected Props and States respectively. Below are the snapshot of the dev tools
React devtool shows the correct Props
Redux devtool show the correct States and Actions
userAction.js
import axios from 'axios';
import * as types from './actionTypes';
export let fetchingUser = () => {
return {
type: types.FETCHING_USERS
};
};
export let fetchedUser = payload => {
return {
type: types.FETCHED_USER,
payload
};
};
export let fetchUser_error = () => {
return {
type: types.FETCH_USER_ERROR
};
};
export let fetchUsers = () => {
let url = 'https://eventcity.herokuapp.com/api/v1/users';
return dispatch => {
dispatch(fetchingUser());
return axios
.get(url)
.then(response => {
const users = response.data.data;
dispatch(fetchedUser(users));
})
.catch(err => {
dispatch(fetchUser_error());
});
};
};
userReducer.js
import * as types from '../actions/actionTypes';
import initialState from './initialState';
const userReducer = (state = initialState, action = {}) => {
switch (action.type) {
case types.FETCHING_USERS:
return { ...state, users: [], error: null, loading: true };
case types.FETCHED_USER:
return { ...state, users: action.payload, error: null, loading: false };
case types.FETCH_USER_ERROR:
return {
...state,
users: [],
error: { message: 'Error loading data from the API' },
loading: false
};
default:
return state;
}
};
export default userReducer;
configureStore.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer from '../reducer/rootReducer';
const configureStore = () => {
return createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
};
export default configureStore;
rootReducer.js
import { combineReducers } from 'redux';
import userReducer from './userReducer';
const rootReducer = combineReducers({
userReducer
});
export default rootReducer;
I think you might want to check this
https://github.com/reactjs/react-redux/issues/129. Your problem is using componentDidMount and componentWillMount without having a better understanding of what they are used for.
The problem is not with redux, all you need to understand is that your fetchUsers request is async and componentDidMount function is only executed once after the component has rendered and it may so happen that the data is not present by the time componentDidMount function is executed and hence your console.log(this.props.users); return empty array, Log it in the render method and you will see the correct data
class UserPage extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.fetchUsers();
}
render() {
console.log(this.props.users);
return (
<div>
<h2>Users Page</h2>
<UserList users={this.props.users} />
</div>
);
}
}
I've spent a couple of days now searching for the answer to this and I still don't know what I'm doing wrong. I have other projects set up in exactly the same way that are fetching data from api's fine. All other answers have said variations of how the actions need to return objects, which as far as I can tell mine are
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import { createLogger } from 'redux-logger';
import Thunk from 'redux-thunk';
import reducer from './reducers/reducer';
import App from './App';
import './css/index.css';
import './css/font-awesome.css';
import './css/bulma.css';
const logger = createLogger();
const store = createStore(reducer, applyMiddleware(Thunk, logger));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
Component calling mapDispatchToProps
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { searchRepositories } from '../actions/searchActions';
class Results extends Component {
componentDidMount() {
this.props.searchRepositories();
}
render() {
return (
<div>Some Stuff</div>
);
}
}
const mapDispatchToProps = (dispatch) => {
return {
searchRepositories: () => {
dispatch(searchRepositories());
},
};
};
const mapStateToProps = (state) => {
return {
repositories: state.repositories,
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Results);
Reducer:
import * as types from '../actions/types';
import initialState from './INITIAL_STATE';
function reducer(prevState = initialState, action) {
if (!action) return prevState;
switch (action.type) {
case types.FETCH_REPOS_REQUEST:
return { ...prevState, loading: true };
case types.FETCH_REPOS_SUCCESS:
return {
...prevState,
loading: false,
repositories: action.data,
error: '',
};
case types.FETCH_REPOS_ERROR:
return { ...prevState, error: 'Encountered an error during GET request' };
default:
return prevState;
}
}
export default reducer;
action creator:
import axios from 'axios';
import * as types from './types';
import { ROOT } from '../config';
export function searchRepositoriesRequest() {
return {
type: types.FETCH_REPOS_REQUEST,
};
}
export function searchRepositoriesSuccess(repositories) {
return {
type: types.FETCH_REPOS_SUCCESS,
data: repositories,
};
}
export function searchRepositoriesError(error) {
return {
type: types.FETCH_REPOS_ERROR,
data: error,
};
}
export function searchRepositories() {
return function (dispatch) {
dispatch(searchRepositoriesRequest());
return axios.get(`${ROOT}topic:ruby+topic:rails`).then((res) => {
dispatch(searchRepositoriesSuccess(res.data));
}).catch((err) => {
dispatch(searchRepositoriesError(err));
});
};
}
I have got this axios api request working using react this.state where I just put it in component did mount. If anyone can see where I am going wrong it would help me out a lot.
Ok, so it looks like I was using an earlier version of redux, which the version of redux-thunk I had installed, didn't like! I updated to the latest version of redux and it now calls as it should.
I'm still not sure why the other projects that have the older version installed still work...
I'm trying to fetch test API using FetchAPI and Redux.
The problem is with dispatch redux action.
Here's my code:
ProductList.js
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as productActions from '../actions/product';
import RaisedButton from 'material-ui/RaisedButton';
function fetchProductsWithRedux() {
console.log("fetchProductsWithRedux-1");
return (dispatch) => {
console.log("fetchProductsWithRedux-2");
dispatch(this.props.action.fetchProdutcsRequest());
return fetchProdutcs().then(([response, json]) => {
if (response.status === 200) {
console.log("success");
dispatch(this.props.action.fetchProductsSucesss(json))
}
else {
console.log("error");
dispatch(this.props.action.fetchProductsError())
}
})
}
}
function fetchProdutcs() {
const URL = "https://jsonplaceholder.typicode.com/posts";
return fetch(URL, { method: 'GET' })
.then(response => Promise.all([response, response.json()]));
}
class ProductList extends Component {
constructor(props) {
super(props);
this.state = {
productList: [
'product 1',
'product 2',
'product 3'
]
}
}
componentDidMount() {
fetchProductsWithRedux();
}
render() {
return (
<div className="ProductList">
<h2>Products</h2>
<ul>
{
this.props.posts &&
this.props.posts.map((post) => {
return (
<li>{post.title}</li>
)
})
}
</ul>
<ol>
<RaisedButton label="Get Products Action" onClick={this.props.action.getProducts} />
</ol>
</div>
);
}
}
function mapStateToProps(state, props) {
return {
product: state.product,
posts: state.posts
};
}
function mapDispatchToProps(dispatch) {
return {
action: bindActionCreators(productActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ProductList);
product.js (actions)
export function fetchProductsRequest() {
console.log('fetchProductsRequest');
return {
type: 'FETCH_PRODUCTS_REQUEST'
}
}
export function fetchProductsSuccess(payload) {
console.log('fetchProductsSuccess');
return {
type: 'FETCH_PRODUCTS_SUCCESS'
}
}
export function fetchProductsError() {
console.log('fetchProductsError');
return {
type: 'FETCH_PRODUCTS_ERROR'
}
}
product.js (reducer)
export default(state = [], payload) => {
switch (payload.type) {
case 'FETCH_PRODUCTS_REQUEST':
console.log('FETCH_PRODUCTS_REQUEST action');
return state;
case 'FETCH_PRODUCTS_SUCCESS':
console.log('FETCH_PRODUCTS_SUCCES action');
return {...state, posts: payload.payload}
default:
return state;
}
};
store.js
import { createStore } from 'redux';
import rootReducer from './reducers';
export default(initialState) => {
return createStore(rootReducer, initialState);
}
Product.js (pages, component)
import React, { Component } from 'react';
import ProductList from '../../components/ProductList';
import RaisedButton from 'material-ui/RaisedButton';
//import './Product.css';
class Product extends Component {
render() {
return (
<div className="Product">
<h1>ProductList Page</h1>
<RaisedButton label="Default" />
<ProductList />
</div>
);
}
}
export default Product;
The line console.log("fetchProductsWithRedux-2"); in ProductList.js has never been reached.
What's wrong? Any ideas? Thanks in advance.
seem you missed import thunk from 'redux-thunk'. You can't return a function in redux action if you dont use any middleware like 'redux-thunk'
There are a few issues with your code.
Firstly, fetchProductsWithRedux is an action, so you would need to dispatch it rather than calling it directly. As Bjoern mentioned, the way you are calling it, the function call just returns a function, which is never called.
Now, you cannot dispatch it in the current scenario, as it returns a function, rather than object. To fix that, you will have to use redux-thunk, which will allow it to dispatch a function.
You can add it to mapDispatchToProps, as you did for getProducts, but there is a shorthand for that. In your mapDispatchToProps, you can do the following:-
const mapDispatchToProps = { fetchProductsWithRedux, getProducts }
You will notice that it is just returning an object with 2 functions.
From the documentation:
If an object is passed, each function inside it is assumed to be a Redux action creator. An object with the same function names, but with every action creator wrapped into a dispatch call so they may be invoked directly, will be merged into the component’s props.
So, it will do exactly what you did earlier with bindActionCreators, but it looks more elegant. Now, instead of onClick={this.props.action.getProducts}, you can do onClick={this.props.getProducts}. Notice the missing action.
Also, your import will change to
import { getProducts } from '../actions/product';
Now, to fix your issue, in componentDidMount, rather than calling the function directly, you will have to do:-
componentDidMount() {
this.props.fetchProductsWithRedux();
}
Hopefully, this will help.
function fetchProductsWithRedux() {
console.log("fetchProductsWithRedux-1");
return (dispatch) => {
console.log("fetchProductsWithRedux-2");
...
}
This returns a function function(dispatch){...}, which is not called in
componentDidMount() {
fetchProductsWithRedux();
}