I have a react app that is pulling down some data, It turns a promise so I am using Thunk. However when I log this.props, the getShift action prints out as a function.
The log returns:
{getShifts: ƒ}getShifts: ƒ ()proto: Object
Action:
import settings from '../../aws-config.js';
import Amplify, { Auth, API } from 'aws-amplify';
export const GET_STAFF_SHIFTS = 'get_staff_shifts';
export const SHIFTS_LOAD_FAIL = 'shifts_load_fail';
export const getShifts = () => dispatch => {
console.log('Fetching list of shifts for user...');
const request = API.get("StaffAPI", "/shifts", {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
})
.then(response =>
dispatch({
type: 'GET_STAFF_SHIFTS',
payload: response
})
)
.catch(err =>
dispatch({type: 'SHIFTS_LOAD_FAIL'})
)
}
reducer:
import { getShifts, GET_STAFF_SHIFTS} from '../actions';
export default function(state = {}, action) {
switch(action.type){
case GET_STAFF_SHIFTS:
return Object.assign({}, state,{
start_time: action.payload
})
default:
return state;
}
}
Action:
import React, { Component } from 'react';
import Amplify, { Auth, API } from 'aws-amplify';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {getShifts} from '../actions/index';
import settings from '../../aws-config.js';
Amplify.configure(settings);
class StaffRota extends Component {
componentWillMount() {
this.props.getShifts();
}
renderPosts(){
console.log(this.props);
return (
<div></div>
);
}
}
function MapDispatchToProps(dispatch) {
return bindActionCreators({ getShifts }, dispatch);
}
export default connect(null, MapDispatchToProps)(StaffRota);
The action creator is supposed to be a function, which is expected. So console logging this.props.getShifts will give you a function.
There are two issues here:
first thing you are dispatching the wrong action type
dispatch({type: 'GET_STAFF_SHIFTS', ... }) instead of dispatch({type: GET_STAFF_SHIFTS, ... }) which you are expecting in your reducer.
secondly you ought to use the redux state via a mapStateToProps function
function MapDispatchToProps(dispatch) {
return bindActionCreators({ getShifts }, dispatch);
}
function MapStateToProps(state) {
return {
shift: state.start_time OR state.your_reducer.start_time
}
}
export default connect(MapStateToProps, MapDispatchToProps)(StaffRota);
And use this state (that is mapped to prop) via this.props.shift.
Related
Still getting used to Redux, first off. I have a component that should simply load data for display when the component loads. I have redux setup with the store:
//store.js
import { createStore, applyMiddleware, compose } from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import root from './reducers';
const middleware = [thunk, logger];
const initState = {};
const store = createStore(
root,
initState,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
and all the reducers that I'll need in a full on combine reducers file:
//{projectFolder}/reducers/index.js
import { combineReducers } from 'redux';
import authReducer from './authReducer';
import errorsReducer from './errorReducer';
import suggestionReducer from './suggestionReducer';
import insiderReducer from './insiderReducer';
import connectionReducer from './connectionReducer';
import outsiderReducer from './outsiderReducer';
import contactReducer from './contactReducer';
import metaReducer from './metaReducer';
export default combineReducers({
auth: authReducer,
errors: errorsReducer,
suggestions: suggestionReducer,
insider: insiderReducer,
connection: connectionReducer,
outsider: outsiderReducer,
contact: contactReducer,
meta: metaReducer
});
The one that I'm interested in is the metaReducer which is the called by an action, or so it should be.
//metaReducer.js
import {GET_INSIDER_META_INFORMATION, GET_OUTSIDER_META_INFORMATION } from '../actions/types';
const initState = {
insider: {},
outsider: {}
};
export default (state = initState, { type, payload }) => {
switch (type) {
case GET_INSIDER_META_INFORMATION:
return{
...state,
insider: payload
}
case GET_OUTSIDER_META_INFORMATION:
return {
...state,
outsider: payload
}
default:
return state;
}
};
The meta reducer is just to house the information coming from the back-end and is each case of the reducer is called from the actions/meta.js file which looks like this:
//{projectfolder}/actions/meta.js
import {
GET_INSIDER_META_INFORMATION,
GET_OUTSIDER_META_INFORMATION,
POPULATE_ERRORS
} from "./types";
import Axios from "axios";
export const getMetaInsider = (dispatch) => {
return Axios.get("meta/insiders")
.then(res =>
dispatch({ type: GET_INSIDER_META_INFORMATION, payload: res.data })
)
.catch(err =>
dispatch({ type: POPULATE_ERRORS, payload: err.response.data })
);
};
export const getMetaOutsider = (dispatch) => {
return Axios.get("meta/outsiders")
.then(res => {
dispatch({ type: GET_OUTSIDER_META_INFORMATION, payload: res.data });
})
.catch(err =>
dispatch({ type: POPULATE_ERRORS, payload: err.response.data })
);
};
and My component that calls all of this is setup as below:
//{projectfolder}/components/home.js
import React, {Component} from 'react';
import {Card, CardTitle, CardSubtitle, CardBody} from 'reactstrap';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {getMetaInsider, getMetaOutsider} from '../actions/meta';
class Home extends Component{
constructor(props){
super(props);
this.state = {
insider:{},
outsider: {}
}
}
componentDidMount() {
console.log(this.props);
this.props.getMetaInsider();
this.props.getMetaOutsider();
}
render(){
let {insiders, outsiders} = this.state;
return(
<React.Fragment>
{*/ omitted as it's not really an issue right now, data is more important than layout /*}
</React.Fragment>
)
}
}
const mapState = state => {
console.log(state);
return {
insider: state.meta.insider,
outsider: state.meta.outsider
}
};
Home.propTypes = {
getMetaInsider: PropTypes.func.isRequired,
getMetaOutsider: PropTypes.func.isRequired,
insider: PropTypes.object.isRequired,
outsider: PropTypes.object.isRequired
};
export default connect(mapState, {getMetaInsider, getMetaOutsider})(Home);
So when the component loads, I get a horribly weird issue where it looks like jquery is being called, and it's imported in my App.js file for bootstrap. However, the main error is this:
"TypeError: dispatch is not a function
at http://localhost:3000/static/js/bundle.js:73524:22"
Which maps up to the .catch block of the getMetaInsider function.
You have to do something like this:
export const getMetaOutsider = () => {
return (dispatch) => {
Axios.get("meta/outsiders")
.then(res => {
dispatch({ type: GET_OUTSIDER_META_INFORMATION, payload: res.data });
})
.catch(err =>
dispatch({ type: POPULATE_ERRORS, payload: err.response.data })
);
}
};
Try this, It should work. Feedbacks are welcome.
redux-thunk handles functions passed as the argument to dispatch instead of objects.
I'm working with React-Native and Redux.
I need to access my User state on react redux after an action.
I try this with a simple console.log after the action on my Component (console.log(this.props.User)) but it is always undefined.
This is my Combine Reducer:
import { combineReducers } from 'redux'; import User from './userReducer';
export default combineReducers({
User });
This is my User Reducer:
import {
REGISTER_USER,
SIGNIN_USER } from '../types';
export default function(state=INITIAL_STATE, action){
switch(action.type){
case SIGNIN_USER:
return {
...state,
userData:{
uid: action.payload.localId || false,
token: action.payload.idToken || false,
refreshToken: action.payload.refreshToken || false,
}
};
break;
default:
return state
} }
This is my action:
export function signIn(data){
const request = axios({
method:'POST',
url:SIGNIN,
data:{
email: data.email,
password: data.password,
returnSecureToken:true
},
headers:{
"Content-Type":"application/json"
}
}).then(response => {
return response.data
}).catch( e=> {
console.log(e)
});
return {
type: SIGNIN_USER,
payload: request
}
}
Component where I try to get the state after action:
import { connect } from 'react-redux';
import { signIn } from '../Store/Actions/userActions';
import { bindActionCreators } from 'redux';
class LoginComponent extends React.Component {
state = {
email:'',
password:''
};
signInUser () {
try {
this.props.signIn(this.state).then( () => {
**console.log(this.props.User)**
})
} catch (error) {
console.log(error)
}
}
}
const mapStateToProps = (state) => {
return{
User: state.User
}
}
const mapDispatchToProps =( dispatch ) => {
return bindActionCreators({signIn}, dispatch)
}
export default connect(mapDispatchToProps,mapDispatchToProps)(LoginComponent);
The response on log just is: undefined
What is my mistake?? Thanks!
The order of the arguments that you are passing into connect method is wrong it expects mapStateToProps first and mapDispatchToProps as second
parameter. So your code has to be export default connect(mapStateToProps, mapDispatchToProps)(LoginComponent);
Here is the signature
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
I'm a bit new to using redux and react. I'm trying to make a simple API call with redux and having it render in react. I can see the API call working as it's in the payload in redux dev tools, but I can't seem to get it to update the state possibly in the `connect?.
actions/index
import FilmAPI from '../api/api';
export const FETCH_FILMS = 'FETCH_FILMS';
export const RECEIVE_FILMS = 'RECEIVE_FILMS';
export const receiveFilms = (films) => {
return {
type: RECEIVE_FILMS,
films
};
}
export const fetchFilmsRequest = () => {
return dispatch => {
return axios.get('https://www.snagfilms.com/apis/films.json?limit=10')
.then(response => {
dispatch(receiveFilms(response.data))
})
}
}
export default fetchFilmsRequest;
reducers/FilmReducer
import RECEIVE_FILMS from '../actions/index';
export function films (state = [], action) {
switch (action.type) {
case RECEIVE_FILMS:
return [...state, action.films];
default:
return state;
}
}
reducers/index
import { combineReducers } from 'redux';
import { films } from './FilmsReducer';
export default combineReducers({
films,
});
containers/FilmListContainer
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchFilmsRequest } from '../actions';
import FilmList from '../components/FilmList'
class FilmListContainer extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.fetchFilmsRequest();
}
render() {
return (
<div>
<FilmList films={this.props.films}/>
</div>
);
}
}
const mapStateToProps = state => ({
films: state.films
})
export default connect(mapStateToProps, {fetchFilmsRequest: fetchFilmsRequest})(FilmListContainer);
configureStore.js
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// options like actionSanitizer, stateSanitizer
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk)
);
return createStore(
rootReducer,
initialState,
enhancer
);
}
As mentioned, Redux DevTools show the films in the payload, but films still remain 0 in its state. Could anyone please point me in the right direction?
You can get updated state by subscribing store and use store.getState()
Steps:
Write subscribe function in constructor of component class.
Set state of class by store.getState().
import store from '../js/store/index';
class MyClass extends Component {
constructor(props, context) {
super(props, context);
this.state = {
classVariable: ''
}
store.subscribe(() => {
this.setState({
classVariable: store.getState().MyStoreState.storeVariable
});
});
}
}
You are close, your action needs to send the data to the store by dispatching an event which your reducer can then catch. This is done using the type attribute on the dispatch object.
https://redux.js.org/basics/actions
return fetch('https://www.snagfilms.com/apis/films.json?limit=10')
.then(response => {
dispatch({
type: RECEIVE_FILMS,
payload: response,
})
})
You then need to grab the data in your reducer and put it in the store
export function films (state = [], action) {
switch (action.type) {
case RECEIVE_FILMS:
return {
...state,
films: action.payload.films
};
default:
return state;
}
}
It looks like you just need to import your action type constant into your reducer using a named import instead of a default export.
i.e. import {RECEIVE_FILMS} from '../actions' rather than import RECEIVE_FILMS from '../actions'
Just dispatch result of resolved fetch promise like so:
if the payload is json, then:
export const fetchFilmsRequest = () => {
return dispatch => {
return fetch('https://www.snagfilms.com/apis/films.json?limit=10')
.then(response => response.json())
.then(response => {
dispatch({
type: RECEIVE_FILMS,
payload: response
})
})
}
Your reducer would need modifying slightly to:
export function films (state = [], action) {
switch (action.type) {
case RECEIVE_FILMS:
return [...action.payload]; // assuming response is jus array of films
default:
return state;
}
}
I have made two api calls in actions. The code for reducer is something like this-
import {combineReducers} from 'redux';
import {GET_API_DATA, GET_QUERY_LIST} from '../actions/index.js';
function getApplicationData(state = [],action){
switch(action.type){
case GET_API_DATA:
return[
...state,
{
resultMeta:action.response,
}
]
default:
return state
case GET_QUERY_LIST:
return[
...state,
{
resultMeta:action.response
}
]
}
}
const data = combineReducers({
getApplicationData
})
export default data;
And in action, I am making a call to the APIs like this-
import * as axios from 'axios';
import {Constants} from '../constants.js';
export const GET_API_DATA = 'GET_API_DATA';
export const getApi = ()=>{
// const res=await axios.get('Constants.URLConst+"/UserProfile"',{headers:{Constants.headers}});
// dispatch({type:GET_API_DATA,payload:res.data});
return(d)=>{
axios({
method:'GET',
url:Constants.URLConst+"/UserProfile",
headers:Constants.headers
}).then((response)=>{
return d({
type:GET_API_DATA,
response
});
}).catch((e)=>{
console.log("e",e);
})
}
}
export const GET_QUERY_LIST='GET_QUERY_LIST';
export const loadData=()=>{
return(d)=>{
axios({
method:'GET',
url:Constants.URLConst+"/Query?pageNum=1&totalperPage=15&userid=0",
headers:Constants.headers
}).then((response)=>{
type:GET_QUERY_LIST,
response
}).catch((e)=>{
console.log(e);
})
}
}
I am calling the loadData() function in a js file something like this-
import React,{Component} from 'react';
import {loadData} from './actions';
import {connect} from 'react-redux';
export class Home extends Component{
componentDidMount(){
this.props.loadData();
}
render(){
return null;
}
}
const mapStateToProps = (state) => {
return{
resultCame: state.getApplicationData
}
}
export default connect(mapStateToProps, {
loadData: loadData
})(Home);
I have two different js files, where I am calling these two functions. While, the first one works fine, for the second one I get the error,
loadData() is not a function.
How can I call multiple functions in redux and what is the problem here??
In your second axios call you need to dispatch the action.
See updated code below
export const loadData = () => {
return (dispatch) => {
axios({
method:'GET',
url:Constants.URLConst+"/Query?pageNum=1&totalperPage=15&userid=0",
headers:Constants.headers
}).then((response)=>{
// remember to dispatch the action once a response is received
dispatch(
type:GET_QUERY_LIST,
response
);
}).catch((e)=>{
console.log(e);
});
}
}
After one of the Redux tutorials decided to implement that facny action->reducer->store->view chain for simple app with only login part.
Seems like all setted up but when I run my app - in mapStateToProps(currentState) no any sign of the any custom state fields which I expected to see! (default state from reducer). But the action function is fine, as you can see on the screenshot
I can't see whats wrong here so, decided to ask it.
So here is the code
So, first of all - store.js
import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers';
import thunk from 'redux-thunk';
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState, applyMiddleware(thunk));
if (module.hot) {
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers');
store.replaceReducer(nextRootReducer);
});
}
return store;
}
then login reducer
const initialState = {
user: {
name: '',
password: ''
},
fetching: false
}
export default function login(state = initialState, action) {
switch (action.type) {
case LOGIN_REQUEST: {
return { ...state, fetching: true }
}
case LOGIN_SUCCESS: {
return { ...state, user: action.data, fetching: false }
}
case LOGIN_FAIL: {
return { ...state, user: -1, fetching: false }
}
default: {
return state;
}
}
}
and the root (reducers/index.js):
import login from './login/login';
import { combineReducers } from 'redux'
export default combineReducers({
login
});
the action
import {
LOGIN_REQUEST,
LOGIN_SUCCESS,
LOGIN_FAIL
} from '../../constants/login.js';
export function onLoginAttempt(userData) {
return (dispatch) => {
dispatch({
type: LOGIN_REQUEST,
user: userData
})
tryLogin(userData);
}
};
function tryLogin(userData) {
let url = 'SignIn/Login ';
return (dispatch) => {
axios.post(url, userData)
.then((response) => dispatch({
type: LOGIN_SUCCESS,
data: response.data
})).error((response) => dispatch({
type: LOGIN_FAIL,
error: response.error
}))
}
};
So here is entrance point:
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './containers/app.js';
import configureStore from './store/configureStore';
let store = createStore(configureStore);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("content")
);
and here is the app.js (Login is just sompe custom div with two fields nothing special)
import React, { Component } from 'react';
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux';
import Login from '../components/login/Login';
import * as pageActions from '../actions/login/login'
class App extends Component {
render() {
const { user, fetching } = this.props;
const { onLoginAttempt } = this.props.pageActions;
return <div>
<Login name={user.name} password={user.password} fetching={fetching} onLoginAttempt={onLoginAttempt} />
</div>
}
}
function mapStateToProps(currentState) {
return {
user: currentState.user,
fetching: currentState.fetching
}
}
function mapDispatchToProps(dispatch) {
return {
pageActions: bindActionCreators(pageActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
I see that you have state which looks like:
reduxState = {
login: {
user: {
name: '',
password: ''
},
fetching: false
}
}
but then you try to access properties that don't exist.
function mapStateToProps(currentState) {
return {
user: currentState.user,
fetching: currentState.fetching
}
}
I think you need to:
function mapStateToProps(currentState) {
return {
user: currentState.login.user,
fetching: currentState.login.fetching
}
}
You have this line let store = createStore(configureStore); on your entry point. However, inside configureStore, you have a call to createStore()
Basically you're calling something like createStore(createStore(reducers)). That's probably the cause of the problem.
You should probably call it like
let store = configureStore( /* pass the initial state */ )