Reputation: 748
I'm new using Redux, and I'm trying to integrate React with Redux. What I want is to put all my actions in one reducer. Actually my reducer looks like this:
import {GET_ALL_CONNECTIONS, DELETE_CONNECTION, POST_CONNECTION} from '../actions';
const initialState = {
}
export default (state = initialState, { type, payload }) => {
switch (type) {
case GET_ALL_CONNECTIONS:
return payload
case POST_CONNECTION:
return {...state, ...payload}
case DELETE_CONNECTION:
return {...state, ...payload}
default:
return state
}
}
The problem is when I call the action corresponding to the GET_ALL_CONNECTIONS type:
export const getAllConnections = () => {
return async (dispatch) =>{
const response = await Conexiones.get('/db/myConnections');
dispatch({type: GET_ALL_CONNECTIONS, payload: response.data});
}
}
When I call this function in a React component is supposed to get multiple connections from an API, and save the array of object resultant of the API call in the state.
The problem is when I want to save the connections array in the state, to later map that state and generate options
with each one of the connection inside a select
element. When I render the component it throws me the next error:
TypeError: this.props.conexiones.map is not a function
The file where I combine all reducers looks like this:
import {combineReducers} from 'redux';
import {reducer as formReducer } from 'redux-form';
import postUser from './postUser';
import postConnection from './postConnection';
import getAllConnections from './getAllConnections';
import ConnectionsReducer from './ConnectionsReducer';
export default combineReducers({
newUser: postUser,
form: formReducer,
conexiones: ConnectionsReducer
});
And the component where I do the call looks like this:
import React, { Component } from 'react';
import { Grid, Container, Select, Button, withStyles, FormControl, InputLabel, MenuItem } from '@material-ui/core';
import {connect} from 'react-redux';
import {reduxForm, Field} from 'redux-form';
import {deleteConnection, getAllConnections} from '../actions';
const styles = theme => ({
root: {
display: 'flex',
flexWrap: 'wrap',
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
});
class BorrarConexion extends Component {
componentDidMount() {
this.props.getAllConnections();
}
handleSubmit = ({conexionId}) => {
this.props.deleteConnection(conexionId);
}
renderConexiones = () => {
return this.props.conexiones.map(conexion =>{
return (<MenuItem key={conexion.id} value={conexion.id}>{conexion.connectionUrl}</MenuItem>);
});
}
renderSelectField = ({input,label,meta: { touched, error },children,...custom}) =>{
return (
<FormControl>
<InputLabel>Seleccione la URL que desea eliminar</InputLabel>
<Select {...input} {...custom}>
{this.renderConexiones()}
</Select>
</FormControl>
)
}
render() {
return (
<Container>
<Grid container direction="column">
<Field name="conexionId" component={this.renderSelectField} label="Favorite Color"/>
<Button onClick={this.props.handleSubmit(this.handleSubmit)}>Eliminar</Button>
</Grid>
</Container>
);
}
}
const mapStateToProps = (state) => {
return {conexiones: state.conexiones}
}
const BorraConexionEstilizado = withStyles(styles)(BorrarConexion);
const formWrapped = reduxForm({form: 'delete_connection'})(BorraConexionEstilizado);
export default connect(mapStateToProps, {getAllConnections, deleteConnection})(formWrapped);
When I do this with a separate reducer called getAllConnections and replace the conexiones: ConnectionsReducers
with conexiones: getAllConnections
it works. The getAllConnections reducer looks like this:
export default (state = [], { type, payload }) => {
switch (type) {
case 'GET_ALL_CONNECTIONS':
return payload
default:
return state
}
}
I want to know how to do this work with one reducer receiving all my actions instead of a individual reducer for each action.
Upvotes: 1
Views: 7885
Reputation: 748
I solved this issue by surrounding the conexion:state.conexion
in the mapStateToProps
method with Object.values()
like this:
conexion: Object.values(state.conexion)
Upvotes: 0
Reputation: 6230
This issue is related to the fact that you are likely returning different structures from your reducer. It looks like the reducer is meant to handle objects as state but for this one case you return an array. Objects do not have a map function. You need to determine the structure of your state for this reducer and stick with it, you cannot change from array to object and back again, that is undeterministic behavior that redux is not built for.
I do not know the implementation details of your APIs but this is the main area in need of updating
const initialState = []
export default (state = initialState, { type, payload }) => {
switch (type) {
case GET_ALL_CONNECTIONS:
return payload //hoping that your api gave an array here
case POST_CONNECTION:
//return {...state, ...payload} //this is clearly returning an object
return [...state, payload] //now it returns an array with a new item
case DELETE_CONNECTION:
//return {...state, ...payload} //this is clearly returning an object
return state.filter(item => item.id !== payload.id) //now it returns a filtered array
default:
return state
}
}
The following code should recieve states as arrays and return updated states as arrays.
Upvotes: 1
Reputation: 397
You have to set initial state... right now, your state is an empty object
Upvotes: 0