Redux action is undefined outside the Promise - reactjs

I've been using async calls with Redux + Promise Middleware like this:
export function getAllFiles() {
const request = axios.get(`${URL}/files`).then(res => res.data);
return { type: "GET_FILES", payload: request };
}
So far it worked, since from my understanding Promise Middleware takes care of this. Also, if I console.log(res.data) inside .then, I will get expected result. However, when I check it from reducer file it's undefined. Can someone please school me if I'm missing something huge.
import React, { Component } from "react";
import { connect } from "react-redux";
import Info from "./../components/Info/info";
import { getAllFiles } from "./../actions";
class HomeContainer extends Component {
componentWillMount = () => {
this.props.dispatch(getAllFiles);
};
render() {
console.log(this.props);
return (
<div>
<Info />
</div>
);
}
}
function mapStateToProps(state) {
return {
files: state.files
};
}
export default connect(mapStateToProps)(HomeContainer);
import { combineReducers } from "redux";
import fileReducer from "./file_reducer.js";
const rootReducer = combineReducers({ wineReducer });
export default rootReducer;
// file_reducer.js
export default function(state = {}, action) {
switch (action.type) {
case "GET_FILES":
return {...state, files: action.payload}
default:
return state;
}
}

The main problem here is your treating, what is clearly an async action, as a synchronous one.
The redux store by default will only deal with action objects, in your case since you need to wait for a result before we fire the action object, we need some middleware to allow the store to handle this.
Have a look at the applyMiddleware function, plenty of links to common thunk middleware and code samples.

Related

Calling action creator inside the function. Error:Actions may not have an undefined "type" property?

I know action creator should have a type of property only then it would be able to dispatch. Since I am having a function call which ultimately leads to one action creator which have type property then Why it is showing me this problem.
When I tried to directly dispatch start game action creator it works but since I have to implement some more function inside them so I needed then inside the function.
How to implement the same?
Menu.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {startGame} from '../actions';
import {loadMenu} from '../actions';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
const page_Banner={
marginTop:'35px',
fontSize:'45px',
textAlign:'center',
letterSpacing:'20px',
fontWeight:'bold'
};
const spacebar_screen={
marginTop:'35px',
color:'grey'
}
class Menu extends Component {
componentDidMount() {
this.props.dispatch(loadMenu());
console.log(this.props.dispatch);
console.log(this.props.isPlaying);
}
render() {
return (
<div style={page_Banner}>
Redux Tetris
{!this.props.isPlaying?<h2 style={spacebar_screen}>Press spacebar to start the game</h2>:null}
</div>
)
}
}
Menu.propTypes={
isPlaying:PropTypes.bool,
}
// function mapDispatchToProps(dispatch){
// return bindActionCreators({loading:loadMenu},dispatch);
// }
const mapStateToProps = (state) => ({
isPlaying: state.gameStatus.currentState !== 'IDLE',
});
export default connect(mapStateToProps)(Menu);
Action.js
import constants from "../gameConstants/constants";
export const startGame=()=>{
const ShapeMapping=constants;
const current_Shapeno=Math.floor(Math.random()*7);
const next_Shapeno=Math.floor(Math.random()*7);
const current_Shape=ShapeMapping[current_Shapeno];
const next_Shape=ShapeMapping[next_Shapeno];
return {
type:"START_GAME",
current_Shape,
next_Shape
};
}
export const pauseGame = () => ({
type: "PAUSE_GAME",
});
export const unpauseGame = () => ({
type: "UNPAUSE_GAME",
});
export const gameOver = () => ({
type: "GAME_OVER",
});
export const loadMenu=()=>({
function(dispatch,getState){
function handleSpacebar(event){
if(event.keyCode==32){
dispatch(loadGame());
window.removeEventListener('keyup',handleSpacebar);
console.log('here')
}
}
window.addEventListener('keyup',handleSpacebar);
}
})
export const loadGame=()=>({
function (dispatch,getState){
dispatch(startGame());
}
})
The issue is in loadMenu and loadGame action creators. You're returning an object with an anonymous function which doesn't make any sense. An action creator is supposed to return an object with a type and the minimal data to define the action and return a function if you're using redux-thunk.
Keep the actions creators clean like you've done in gameOver and handle everything else in reducers or using the redux pub/sub pattern.
See this answer by Dan Abramov https://github.com/reduxjs/redux/issues/787

Dispatched action received by reducer, but not appearing in action list and not rerendering

I have a Comment component that has a delete button. When the button is clicked, it called the action, makes the axios call, and when the call returns it dispatches the update to the reducer. The only problem is that it's not triggering the rerender of the parent component. The action, although it is updating the state, does not appear in the list of dispatched actions in the Redux DevTools. All other actions work and display in the DevTools, but for some reason this one doesn't.
My thought after reading the comment section below is that it's because I'm making a shallow copy of my object. Am I wrong to think that making a shallow copy, modifying a deeper object, and returning the shallow copy wouldn't trigger a rerender? Isn't the fact that the shallow object is a different reference enough to trigger it? I'm confident I'm doing this the same way in other places and I havenn't have a problem elsewhere. It
This is the action list in Redux DevTools after deleting the comment. I would expect that it would have "delete_comment" near the bottom somewhere, but it's not:
The data is passed from the parent components CommentList -> CommentThread -> Comment.
Is this truly dispatching?
This is a simplified component:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {
deleteComment,
} from "../../actions/comment_actions";
class Comment extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
const {comment, data} = this.props;
if (!data) {
//console.log("mir_data doesnt exist");
return <div/>;
}
return (
<div key={"comment" + comment.id} id={"c" + comment.id}>
<button onClick={() => this.props.deleteComment(comment.id)}>Delete</button>
</div>
);
}
}
function mapStateToProps(state) {
return {
user: state.user
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
deleteComment,
}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(Comment);
Here's a simplified action file:
export const DELETE_COMMENT = 'delete_comment';
export function deleteComment(id, callback = null) {
return (dispatch, getState) => {
axiosInstance.post('/delete-comment/', {comment_id: id}, getState().api.axios).then(
response => {
dispatch(dispatchDeleteComment(id));
//dispatch(dispatchViewUserInfo(response.data));
if (response.status === 200)
callback();
}
);
}
}
export function dispatchDeleteComment(id) {
return {
type: DELETE_COMMENT,
payload: id
};
}
Here's the simplified reducer:
import {DELETE_COMMENT} from "../actions/comment_actions";
export default function(state = {}, action){
let newState = {...state};
switch(action.type){
case DELETE_COMMENT:
//some logic
delete newState.comments[action.payload];
return newState;
default:
return state;
}
}

Loading Data from MongoDB into Redux Store fetching from my node API

I am working on my very first react app, and I have successfully setup my Node API and MongoDB, and am now trying to integrate redux into my application. I will try to share the relevant code snippets here.
First, in my node API, I have a model mlb_ids_logos_colors.js with some baseball data. My react app is currently getting this data using the following fetch using an async function:
export async function get_mlb_ids_logos_colors() {
return fetch('/mlb/mlb_ids_logos_colors')
.then(resp => {
return resp.json();
})
}
I would like to get the data from this endpoint into my redux store, and then from the redux store into the various components that will use it, but I am a bit stuck. As far as redux, I have the following files:
reducers/index.js (my rootReducer)
import { combineReducers } from 'redux';
import mlbIdsLogosColorsReducer from './mlb-ids-logos-colors-reducer';
export default combineReducers({
mlbIdsLogosColorsReducer
})
reducers/mlb-ids-logos-colors-reducer.js
export default function reducer (state={
mlbIdsLogosColorsData: [],
}, action) {
switch (action.type) {
case "FETCH_COLORS": {
return {...state, mlbIdsLogosColorsData }
}
}
return state;
}
actions/mlb-ids-logos-colors-action.js
export const FETCH_COLORS = "FETCH_COLORS";
export function fetchMlbIdsLogosColors() {
return function(dispatch) {
dispatch({type: "FETCH_COLORS"});
fetch('/mlb/mlb_ids_logos_colors')
.then(resp => {
return resp.json();
})
}
}
lastly, I setup my store in **store.js* as follows, and import this into my apps main index.js file:
store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
Any help with this is appreciated. For reference, I am receiving the following error when launching the app:
./src/reducers/mlb-ids-logos-colors-reducer.js
Line 7: 'mlbIdsLogosColorsData' is not defined no-undef
I'm aware this is a quite-obvious error message, but admittidly I'm not sure where I should be defining mlbIdsLogosColorsData.
Thanks in advance!
EDIT: I don't have to make any changes ever to the data at my /mlb/mlb_ids_logos_colors endpoint. I just want to get this data into the redux store, and then from the redux store into the components. I know to use mapStateToProps() and connect() in my components to get the data into the components.
EDIT2: I HAVE DIFFERENT NAMES FOR THE ACTION! let me fix that, and see if that resolves the issue!
I'm in a hurry sorry if I misleading you but roughly you are dispatching an action without data. You should use something like that in your action creator:
export function fetchMlbIdsLogosColors() {
return function(dispatch) {
fetch('/mlb/mlb_ids_logos_colors')
.then(resp => {
return resp.json()
.then( json => dispatch({type: "FETCH_COLORS", json}));
})
}
}
and then use this payload (json) in your reducer like that:
export default function reducer (state={
mlbIdsLogosColorsData: [],
}, action) {
switch (action.type) {
case "FETCH_COLORS": {
return {...state, mlbIdsLogosColorsData: action.json }
}
}
return state;
}
Again, this is a rough suggestion. I did not check your whole code. But you are getting undefined error since there is not a variable named mlbIdsLogosColorsData right now.

React-Redux - failing to show props on view page

EDIT: Ive changed my return to an object, yet still i'm getting empty props.
console.log of mapStateToProps(state) shows that temperature is empty. I am assuming I am getting back a non changed state, temperature from my axios call is not returned to my weatherPage.js.
my whole environment is working fine, im just trying to make an axios get request.
I'm having a bit of a difficulty passing my object through the Redux lifecycle, from the actions to the reducer while keeping the propTypes validation method and trying to use Object.assign() (which is truly the right way of mutating the state with a single deep copy as Dan Abramov noted.)
The error:
my props are empty. The axios call I've made in src/actions/weatherActions.js is not showing up as a prop.weatherDetails.temperature in src/components/weatherPage.js
, it returns my default state.
I'm new to ES6 and Redux, I've included propTypes into my page and I'm having bit of an issue with this, I think the issue comes from supplying the right state that comes from the action.
When the choose button is pressed i'm supposed to receive the temp_c (axios calls this json)
src/components/weatherPage.js
import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import {withRouter} from 'react-router';
import * as WeatherActions from '../../actions/weatherActions';
class WeatherPage extends React.Component {
render() {
return (
<div>
<h2>temps: {this.props.weatherDetails.temperature}</h2>
<input
type="submit"
onClick={this.onClickSave.bind(this)}
value="CHOOSE"/>
</div>
);
}
onClickSave() {
WeatherActions.getWeather(this.props.dispatch);
}
WeatherPage.propTypes = {
weatherDetails: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired
};
function mapStateToProps(state) {
return {
weatherDetails: state.weathers.weatherDetails
};
}
export default connect(mapStateToProps)(withRouter(WeatherPage));
Since this.props.weatherDetails.temperature shows my current state, I know that problem lies between the action and the reducer.
src/actions/weatherActions.js
import axios from 'axios';
export const ActionTypes = {
WEATHER: { LOAD_WEATHER: 'WEATHER.LOAD_WEATHER' } };
export function getWeather(dispatch) {
console.log('in getWeather method');
console.log('this is getWeather dispatch: ', dispatch);
axios({
url: 'http://api.weatherunlocked.com/api/trigger/32.08,34.78/current%20temperature%20gt%2016%20includecurrent?app_id=ba2f68f0&app_key=0356747cc4d1d4ba0dd5cc25a0c86743',
method: 'get'
}).then(function (response) {
//console.log('in the then func with this res: ', JSON.stringify(response));
dispatch({
type: ActionTypes.WEATHER.LOAD_WEATHER,
temperature: response.CurrentWeather.temp_c
},
function () {
console.log('dispatch completed');
});
});
console.log('end of class getWeather');
I'm performing a simple axios call, yet i'm not sure if i'm dispatching the 'payload' (temperature: response.CurrentWeather.temp_c) correctly to appear through the reducer and back into view.
Here is my reducer:
src/reducers/weatherReducer.js
import * as WeatherActions from '../actions/weatherActions';
const initialState = {
weatherDetails: {
area: '',
temperature: 'sdf'
}
};
function WeatherReducer(state = initialState, action) {
console.log('in WeatherReducer, this is action: ' + JSON.stringify(action));
switch (action.type) {
case WeatherActions.ActionTypes.WEATHER.LOAD_WEATHER:
return [...state, Object.assign({}, action.temperature.data)];
default:
return state;
}
}
export default WeatherReducer;
What am I missing here on this build?
You state object must be returned as an object rather than an array from the reducer like
switch (action.type) {
case WeatherActions.ActionTypes.WEATHER.LOAD_WEATHER:
return Object.assign({}, state, action.temperature.data);
default:
return state;
}

Redux woes: switch statement not registering action.type and not executing correct action

I am new with Redux and, to be honest, find it to be rather difficult and not intuitive at all to use. What I am trying to accomplish is just to load data from a file inside my app into a view just to get reducers and actions working correctly. I have a switch statement inside a projects_reducer.js file, an actions index.js, a projects.js view, and a reducer index.js as well. I've logged it out and the switch statement seems to be skipping the Fetch_Projects action every time and going to the default because action.type is not being registered correctly. I don't know why this is. Any help is appreciated. My code is included below.
//actions index.js file, only top two actions are relevant
import {Registrants, Projects} from '../../data';
export const Fetch_Users="Fetch_Users";
export const Fetch_Projects="Fetch_Projects";
export const Add_Project="Add_Project";
export const Add_User="Add_User";
export const Add_File="Add_File";
export const Delete_User="Delete_User";
export const Delete_Project="Delete_Project";
export const Delete_File="Delete_File";
export function fetchUsers(){
return {
type: Fetch_Users,
payload: Registrants
}
}
export function fetchProjects(){
console.log("the fetch projects action!");
return {
type: Fetch_Projects,
payload: Projects
}
}
export function addUser(){
return {
type: Add_User,
payload: "stuff"
}
}
export function addProject(){
return {
type: Add_Project,
payload: "stuff"
}
}
export function addFile(){
return {
type: Add_File,
payload: "stuff"
}
}
export function deleteUser(){
return {
type: Delete_User,
payload: "stuff"
}
}
export function deleteProject(){
return {
type: Delete_Project,
payload: "stuff"
}
}
export function deleteFile(){
return {
type: Delete_File,
payload: "stuff"
}
}
projects_reducer.js file
import {Fetch_Projects, Add_Project, Delete_Project} from '../actions';
import {Registrants, Projects} from '../../data';
export default function(state={}, action){
console.log("payload", action);
switch(action.type){
case Fetch_Projects:
return Projects;
case Add_Project:
return state;
case Delete_Project:
return state;
default:
console.log("Well, you hit the default... :(")
return state;
}
}
reducers index.js
import {combineReducers} from 'redux';
import {reducer as formReducer} from 'redux-form';
import ProjectsReducer from './projects_reducer';
const rootReducer=combineReducers({
projects: ProjectsReducer
//form: formReducer
});
export default rootReducer;
projects.js view
import React, {Component} from 'react';
import {connect} from 'react-redux';
import Table from './table';
import {fetchProjects} from '../actions';
class Projects extends Component{
componentDidMount(){
fetchProjects();
console.log("props", this.props);
}
render(){
return (
<div>
<Table />
</div>
)
}
}
function mapStateToProps(state){
return {projects: state.projects}
}
export default connect(mapStateToProps, {fetchProjects})(Projects);
In order to get an action to call your reducer and get the expected result, you must call your action by dispatching your action. In docs you can see this.
This is the only way to trigger a state change.
In your componentDidMount where you call fetchProjects, you are just calling a function, but not dispatching a function.
What you want to do is define the mapDispatchToProps function and attach the dispatch call to you fethcProjects function there.
Here is an example if that.
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Object.assign({}, todoActionCreators, counterActionCreators), dispatch)
}
}
Although this example shows the usage of bindActionCreators, I should point out that there are ways of doing it without that which you can find in the docs.
The docs for this can be found here.
Alternatively, you can just bring in your store directly and call store.dispatch(fetchProjects()), but this is not the standard approach and I am merely demonstrating the usage of dispatch.
To anyone who might reference this question later, I did manage to solve this problem and the difficulty seems to have come from how I was calling the fetchProjects action in the projects.js view. I was calling it as just fetchProjects(), while the correct call was this.props.fetchProjects(). This must mean that react-redux adds the function(s) that you supply as mapDispatchToProps to props for you to call, although this is pretty tricky to a novice or the react-redux uninitiated.

Resources