I am not able to understand why reducer is being called in one case and not in another case. The footer component is given below.
File Footer.jsx
import React from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import './Footer.scss';
import * as footerActions from '../../redux/actions/footer';
const mapStateToProps = (state) => {
return state;//{state: state, footerState: getFooterData(state)};
};
// Footer component is aware of only footer actions
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(footerActions, dispatch)
};
};
var Footer = React.createClass({
propTypes:{
state: React.PropTypes.object,
actions: React.PropTypes.object
},
componentWillMount() {
this.props.actions.changeLanguage("dutch");//<-- This calls reducer and changes language
},
changeLang(language) {
this.props.actions.changeLanguage("spanish");//<--- This doesn't calls reducer
},
render() {
var that = this;
return (
<div className="footer">
<div onClick={that.changeLang.bind(that, 'english')}>English</div>
<div onClick={that.changeLang.bind(that,'german')}>German</div>
</div>
);
}
});
export default connect(mapStateToProps, mapDispatchToProps)(Footer);
actions file footer
export const LANGUAGE_CHANGE = 'LANGUAGE_CHANGE';
export function changeLanguage(data, context) {
return {
type: LANGUAGE_CHANGE,
data
};
}
Reducer file footer
import { LANGUAGE_CHANGE } from '../actions/footer';
export default function footer(state = {}, action) {
switch (action.type) {
case LANGUAGE_CHANGE:
debugger;
return Object.assign({}, state, {language:action.data});
default:
return state;
}
}
In footer.jsx component code, dispatching LANGUAGE_CHANGE action from componentDidMount method successfully calls footer reducer but, reducer is not called when LANGUAGE_CHANGE action dispatched from changeLang method. Not able to figure out what is going wrong ? Help Appreciated.
Related
I just tried make simply reducer in react redux but it never called. After a lot trial i have no idea why it's not working. console.log in action is showing but reducer never is called.
import React from "react";
import { connect } from "react-redux";
import * as actions from "store/actions";
function Login(props) {
const login = (e) => {
e.preventDefault();
props.login();
};
return (
<form onSubmit={login}>
<button> login </button>
</form>
);
}
const mapDispatchToProps = (dispatch) => {
return {
login: () => dispatch(actions.login),
};
};
export default connect(null, mapDispatchToProps)(Login);
actions file- i'm here console.log is showing correctly
import * as actionsTypes from "./actionTypes";
export const logout = () => {
return {
type: actionsTypes.AUTH_LOGOUT,
};
};
export const login = () => {
console.log("i'm here")
return {
type: actionsTypes.AUTH_LOGIN,
};
};
reducer
import * as actionTypes from "../actions/actionTypes";
const initialState = {
isLogged: false,
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.AUTH_LOGIN:
return {
...state,
isLogged: true,
};
case actionTypes.AUTH_LOGOUT:
return {
...state,
isLogged: false,
};
default:
return state;
}
};
export default reducer;
many thanks for help
Probably, you forget to make a configuration of the store itself? :)
Something like that:
// at configureStore.js
import { createStore } from 'redux';
import reducer from '../path/to/your/root/reducer'; // assuming that you use combineReducer function to gather all reducers in one place
export default createStore(reducer);
Then in your app root you need to wrap entry component with the store provider:
import store from './store/configureStore';
import { Provider } from 'react-redux';
export default () => (
<Provider store={store}>
<AppRootComponent />
</Provider>
);
AppRootComponent -> your initial app component
For reference - how to configure store
UPD:
Looks like you were trying to pass the action creator to the dispatch function, instead of invoking it actually. Just make a call of that creator in the dispatch:
login: () => dispatch(actions.login()),
BTW, here is the working example of your case
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";
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();
}
I try to deal with ajax data in my learning react,redux project and I have no idea how to dispatch an action and set the state inside a component
here is my component
import React, {PropTypes, Component} from 'react';
import Upload from './Upload';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import * as profileActions from '../../../actions/profileActions';
class Profile extends Component {
static propTypes = {
//getProfile: PropTypes.func.isRequired,
//profile: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
this.state = {
profile:{
username: '',
password: ''
}
}
this.onUpdate = this.onUpdate.bind(this)
}
onUpdate(event) {
alert()
}
componentWillMount() {
}
componentDidMount() {
}
render() {
const {profile} = this.props;
return (
<div>
</div>
);
}
}
function mapStateToProps(state) {
console.log(state)
return {
profile: state.default.profile,
};
}
function mapDispatchToProps(dispatch, ownProps) {
return {
actions: bindActionCreators(profileActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Profile);
I create the store as it follows
import { createStore, combineReducers, applyMiddleware } from 'redux'
import createLogger from 'redux-logger'
import thunk from 'redux-thunk'
import { routerReducer, routerMiddleware, push } from 'react-router-redux'
import reducers from '../reducers'
import { browserHistory } from 'react-router';
const middleware = [ thunk ];
if (process.env.NODE_ENV !== 'production') {
middleware.push(createLogger());
}
middleware.push(routerMiddleware(browserHistory));
// Add the reducer to your store on the `routing` key
const store = createStore(
combineReducers({
reducers,
routing: routerReducer
}),
applyMiddleware(...middleware),
)
export default store;
reducer
export const RESOLVED_GET_PROFILE = 'RESOLVED_GET_PROFILE'
const profileReducer = (state = {}, action) => {
switch (action.type) {
case 'RESOLVED_GET_PROFILE':
return action.data;
default:
return state;
}
};
export default profileReducer;
actions
import * as types from './actionTypes';
import Api from '../middleware/Api';
export function getProfile() {
return dispatch => {
dispatch(setLoadingProfileState()); // Show a loading spinner
Api.getAll('profile').then(profile => {
dispatch(doneFetchingProfile(profile));
}).catch(error => {
throw(error);
});
/*Api.fetch(`profile`, (response) => {
console.log(response)
dispatch(doneFetchingBook()); // Hide loading spinner
if(response.status == 200){
dispatch(setProfile(response.json)); // Use a normal function to set the received state
}else {
dispatch(error)
}
}) */
}
}
function setProfile(data) {
return {type: types.SET_PROFILE, data: data}
//return { type: types.SET_PROFILE, data: data };
}
function setLoadingProfileState() {
return {type: types.SHOW_SPINNER}
}
function doneFetchingProfile(data) {
console.log(data)
return {
type: types.HIDE_SPINNER,
profile: data
}
}
function error() {
return {type: types.SHOW_ERROR}
}
but I have no idea how would I dispatch action and update the state after getProfile action
You need to only dispatch your event RESOLVED_GET_PROFILE right after dispatching doneFetchingProfile, or simply listen RESOLVED_GET_PROFILE and hide spinner on reducing it.
Api.getAll('profile').then(profile => {
dispatch(doneFetchingProfile(profile));
dispatch(resoloveGetProfile(profile));
})
Actually you r doing everything right - so I didn't understand what is your question is, so if you meant something else - let me know, I`ll try to describe you.
About dispatch(resoloveGetProfile(profile));
There you dispatch action, which will update your state, simple as you do with some static state, I saw that you already have setProfile action, so you can change that line, to call your existed function.
dispatch(setProfile(profile))
Than you need to reduce your state in this action
case 'SET_PROFILE' : (action, state) => {...state, profile: action.data}
Than your state will change and your components will update. Note that your 'get profile method' better to call from componentDidMount to avoid freezing at rendering because of performing web request.
I am standing in a tricky situation.
I my reducer rhythmReducer.js is the following:
import {TOGGLE_NOTE_VALUE} from '../constants/actionTypes';
import objectAssign from 'object-assign';
import initialState from './initialState';
export default function rhythmReducer(state = initialState.rhythm, action) {
let newState = objectAssign({}, state);
console.log("---RhythmReducer");
console.log(action.type);
switch (action.type) {
case TOGGLE_NOTE_VALUE:
console.log("TOGGLE_NOTE_VALUE");
return newState;
default:
return newState;
}
}
The component using it is RhythmContainer.js:
import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as actions from '../actions/rhythmActions';
import {Meter} from './Meter';
export const RhythmContainer = (props) => {
let rows = [];
for (let i=0; i < props.rhythm.meters.length; i++) {
rows.push(<Meter key={i} actions={actions} rhythm= {props.rhythm.meters[i]}/>);
}
const handleClick = () => {
return props.store.dispatch(actions.toggleNoteValue);
};
return (
<div onClick={handleClick}>
This will be a 4/4 rhythm
{rows}
</div>
);
};
RhythmContainer.propTypes = {
rhythm: PropTypes.object.isRequired,
store: PropTypes.object.isRequired,
};
function mapStateToProps(state) {
return {
rhythm: state.rhythm,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(RhythmContainer);
My action is defined in rhythmActions.js
import * as types from '../constants/actionTypes';
export function toggleNoteValue() {
console.log("toggleNoteValue");
return {type: types.TOGGLE_NOTE_VALUE};
}
Even though the reducer runs when the page is initializing I can not get it to run when I click on the div.
toggleNoteValue() is firing up but it never goes in the actual Reducer.
Any help?
PS the full project is here just in case it helps: https://github.com/ichionid/rhythmGeneratorReact/tree/master/src
Here are a couple things to try.
In your project, configureStore.js imports a rootReducer from
"../rootReducer", but there's no such module. I'm not sure if this is
just a commit issue, but it's worth checking.
The argument to dispatch should be an action. actions.toggleNoteValue
is not an action, it's a function that returns an action. Try
props.store.dispatch(actions.toggleNoteValue()) or
props.actions.toggleNoteValue() instead.
I sometimes notice this problem when reducers don't fire because they've not been put through mapDispatchToProps correctly:
// WRONG
import { action } from './actions'
// action will still fire as a function, but that's it
const Comp = ({ label }) => <button onClick={() => action()}>{label}<button>
export default connect(mapStateToProps, { action })
// RIGHT
import { action } from './actions'
// action is sent in as a prop meaning we use the connected version rather than the action directly
const Comp = ({ action, label }) => <button onClick={() => action()}>{label}<button>
export default connect(mapStateToProps, { action })