POST http://localhost:3000/api/courses/[object%20Object]/units 404 (Not Found) - javascript

(Only my 3rd post here, so please excuse any blatant issues).
The following is my Unit component, a child of a Course component (courses has_many units).
import React from 'react';
import { connect } from 'react-redux';
import { getUnits, addUnit, updateUnit } from '../reducers/units';
import { Container, Header, Form } from 'semantic-ui-react';
class Units extends React.Component {
initialState = { name: ''}
state = { ...this.initialState }
componentDidUpdate(prevProps) {
const { dispatch, course } = this.props
if (prevProps.course.id !== course.id)
dispatch(getUnits(course.id))
}
handleSubmit = (e) => {
debugger
e.preventDefault()
debugger
const unit = this.state
const { dispatch } = this.props
if (unit.id) {
debugger
dispatch(updateUnit(unit))
} else {
debugger
dispatch(addUnit(unit))
this.setState({ ...this.initialState })
}
}
handleChange = (e) => {
const { name, value } = e.target
this.setState({ [name]: value })
}
units = () => {
return this.props.units.map( (unit, i) =>
<ul key={i}>
<li key={unit.id}> {unit.name}</li>
<button>Edit Module Name</button>
<button>Delete Module</button>
</ul>
)
}
render() {
const { name } = this.state
return (
<Container>
<Header as="h3" textAlign="center">Modules</Header>
{ this.units() }
<button>Add a Module</button>
<Form onSubmit={this.handleSubmit}>
<Form.Input
name="name"
placeholder="name"
value={name}
onChange={this.handleChange}
label="name"
required
/>
</Form>
</Container>
)
}
}
const mapStateToProps = (state) => {
return { units: state.units, course: state.course }
}
export default connect(mapStateToProps)(Units);
The following is its reducer:
import axios from 'axios';
import { setFlash } from './flash'
import { setHeaders } from './headers'
import { setCourse } from './course'
const GET_UNITS = 'GET_UNITS';
const ADD_UNIT = 'ADD_UNIT';
const UPDATE_UNIT = 'UPDATE_UNIT';
export const getUnits = (course) => {
return(dispatch) => {
axios.get(`/api/courses/${course}/units`)
.then( res => {
dispatch({ type: GET_UNITS, units: res.data, headers: res.headers })
})
}
}
export const addUnit = (course) => {
return (dispatch) => {
debugger
axios.post(`/api/courses/${course}/units`)
.then ( res => {
dispatch({ type: ADD_UNIT, unit: res.data })
const { headers } = res
dispatch(setHeaders(headers))
dispatch(setFlash('Unit added successfully!', 'green'))
})
.catch( (err) => dispatch(setFlash('Failed to add unit.', 'red')) )
}
}
export const updateUnit = (course) => {
return (dispatch, getState) => {
const courseState = getState().course
axios.put(`/api/courses/${course.id}/units`, { course })
.then( ({ data, headers }) => {
dispatch({ type: UPDATE_UNIT, course: data, headers })
dispatch(setCourse({...courseState, ...data}))
dispatch(setFlash('Unit has been updated', 'green'))
})
.catch( e => {
dispatch(setHeaders(e.headers))
dispatch(setFlash(e.errors, 'red'))
})
}
}
export default (state = [], action) => {
switch (action.type) {
case GET_UNITS:
return action.units;
case ADD_UNIT:
return [action.unit, ...state]
case UPDATE_UNIT:
return state.map( c => {
if ( c.id === action.unit.id )
return action.unit
return c
})
default:
return state;
}
};
Note: My reducer is working for my getUnits and rendering the units properly.
Note also: when I try to submit a new unit, it ignores all of the debuggers in my handleSubmit and the debuggers in my addUnits (in the reducer), but somehow renders the flash message of "Failed to add units".
Then the console logs the error seen in the title of this post.
I raked my routes and my post is definitely supposed to go to the route as it is.
I have tried passing in the unit and the course in various ways without any change to the error.
How can it hit the flash message without hitting any of the debuggers?
How do I fix this [object%20Object]issue?
Thanks in advance!

The variable course in the following line
axios.get(`/api/courses/${course}/units`)
is an object. When you try to convert an object to a string in JavaScript, [object Object] is the result. The space is then converted to %20 for the URL request.
I would look at the contents of the course variable. Likely, what you actually want in the URL is something inside of course. Perhaps course.id.
If you are still having issues, you'll need to explain what value should go in the URL between /courses/ and /units, and where that data exists.

You are invoking addUnit and updateUnit with a parameter that is equal to this.state in handleSubmit
const unit = this.state
addUnit(unit)
As this.state is of type object, it is string concatenated as object%20object.
getUnit works fine as the parameter passed there comes from the prop course. Check the value of state inside handleSubmit.

Related

TypeError: Products.find is not a function in( react/typescript)

I use redux thunk
why? find is notFunction
on console log redux given data
help pls
I have a strange problem because even data is sent inside the console and I can access the data but can not find in Redax even the initial value of the Hats object
my dependency
import React, { useEffect } from 'react';
import * as b from 'react-bootstrap';
import { useHistory } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux'
import { productsDetailActions } from '../StateManagement/Actions/productActions';
type Props = {
match: any;
params: any;
pro: any,
}
interface PropsInterface {
match: Props;
}
const SingleProduct = ({ match }: PropsInterface) => {
const dispatch = useDispatch()
console.log(match.params.id)
useEffect(() => {
dispatch(productsDetailActions(match.params.id))
}, [])
const Products = useSelector((state: any) => (state.productDetail.product))
console.log(Products)
const product = Products.find((item: any) => {
return item._id === match.params.id
})
console.log(product)
const history = useHistory()
return (
<div>
<b.Row className='my-2 '>
<b.Col>
<b.Button className='btn-danger' onClick={() => history.goBack()}>بازگشت</b.Button>
</b.Col>
</b.Row>
<b.Row>
<b.Col md={5}>
<b.Card className="shadow-sm p-3 mb-5 bg-white">
<b.Card.Body className=''>
<b.Image src={product && product?.image} fluid />
<b.Card.Text className="text-center">{product && product?.description}</b.Card.Text>
</b.Card.Body>
<b.Card.Body className="text-muted">
<h3 style={{ textAlign: 'center' }}>{product && product?.name}</h3>
</b.Card.Body>
</b.Card>
</b.Col>
<b.Col xs={5} md>
<b.Card className='shadow-sm p-3 mb-5 bg-white'>
<b.Card.Header>
<h5 style={{ textAlign: 'center' }}>{product && product?.name}</h5>
</b.Card.Header>
<b.Card.Body>
<b.ListGroup variant='flush'>
<b.ListGroup.Item>
{product && product?.description}
</b.ListGroup.Item>
<b.ListGroup.Item>
{product && product?.name}
</b.ListGroup.Item>
</b.ListGroup>
</b.Card.Body>
<b.Card.Footer>
<b.Button type='button' className='btn btn-block' >خرید محصول</b.Button>
</b.Card.Footer>
</b.Card>
</b.Col>
</b.Row>
</div>
)
}
export default SingleProduct
How can I prevent such problems from occurring? I have a similar one even on the map method
and my reducers are the following:
interface ActionInterface {
payload?: any,
type?:string
}
type state = {
state: any,
loading: boolean,
products?:any
}
const initialState = {
products: [],
loading:false,
}
export const productReducers = (state=initialState,action:ActionInterface) => {
switch (action.type) {
case 'send_req':
return { loading: true, products: [] }
case 'req_success':
return { loading: false, products: action.payload }
default:
return state
}
}
export const productDetailReducers = (state={product:{}},action:ActionInterface) => {
switch (action.type) {
case 'send_req_detail':
return { loading: true, ...state}
case 'req_success_detail':
return { loading: false, product: action.payload }
default:
return state
}
}
and these are the actions:
import axios from 'axios'
export const productsActions = () => async (dispatch: any) => {
try {
dispatch({ type: 'send_req' })
const response = await axios.get('http://localhost:8001/api/products')
dispatch({ type: 'req_success', payload: response.data})
}catch (err) {
console.log(err)
}
}
export const productsDetailActions = (id:any) => async (dispatch: any) => {
try {
dispatch({ type: 'send_req_detail' })
const response = await axios.get(`http://localhost:8001/api/products/${id}`)
dispatch({ type: 'req_success_detail', payload: response.data })
console.log(response.data)
}catch (err) {
console.log(err)
}
}
There's a chance you are using the wrong keys to grab something from your redux store.
You are seeing this error: Product.find is not a function because your Product is likely not an array.
I can affirm the above looking at your reducer function for product detail:
export const productDetailReducers = (state={product:{}},action:ActionInterface) => {
Notice that the initial state value here is: product:{}.
hence const Products = useSelector((state: any) => (state.productDetail.product)) will return {} which DOESN'T have a find method.
Review these lines:
const Products = useSelector((state: any) => (state.productDetail.product))
console.log(Products)
const product = Products.find((item: any) => {
return item._id === match.params.id
})
And maybe set product=[] here:
export const productDetailReducers = (state={ product:[] }, action: ActionInterface) => {
It'll be helpful if you could share a snippet of your store object.
Tip: you should really ever use any in TS as last resort.

TypeError: updateElement is not a function

I am trying to update an element from an array by adding an object as a property like shown in this picture
When a user clicks on a single node button, a modal appears the user fills the form and then it is addes as a property for this node.
But for some reason I get this type error that says that the updateElement is not a function.
BTW, I am using Redux & react-flow-renderer libraries.
Reducer
import * as types from '../actions/types';
const initialState = {
elements: []
};
const flow = (state = initialState, action) => {
switch (action.type) {
case types.UPDATE_ELEMENT:
return {
...state,
elements: state.elements.map((e) => {
if (e.id === action.payload.id) {
e = {
...e,
options: action.payload.options,
};
}
return e;
}),
};
default:
return state;
}
};
export default flow;
Action
import { UPDATE_ELEMENT } from './types';
export const updateElement = (data) => (dispatch) => {
dispatch({
type: UPDATE_ELEMENT,
payload: data,
});
};
Node modal
import React, { useState } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { updateElement } from '../../../../redux/actions/flow';
const VPCNodeModal = (props, { updateElement }) => {
const [formData, setFormData] = useState({
instance: '',
});
// options
const { instance } = formData;
const onFormChange = (e) =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmitForm = () => {
const update = {
id: selectedElement.id,
options: formData,
};
updateElement(update);
};
return (
<>
<Modal {...props}>
<form
onSubmit={(e) => {
e.preventDefault();
onSubmitForm();
}}
>
<label>
<span> Instance name:</span>
<input
type='text'
name='instance'
value={instance}
onChange={onFormChange}
/>
</label>
<button type='submit'>Submit</button>
</form>
</Modal>
</>
);
};
VPCNodeModal.propTypes = {
updateElement: PropTypes.func.isRequired
};
export default connect(null, { updateElement })(VPCNodeModal);
Issue is while receiving the props.
change
const VPCNodeModal = (props, { updateElement }) => {
to
const VPCNodeModal = (props) => {
const { updateElement } = props;
updateElement is a props was passes in VPCNodeModal. So you should update like this with spread operator
const VPCNodeModal = ({ updateElement, ...props }) => {

Redux action doesn't dispatch after page refresh

I have an issue with redux and probably useEffect(I am not sure where my mistake is). I am trying to get information from PokeAPI and store information in the redux state. The problem is that the information about pokemons don't include pokemon types(fire, water, etc.), to solve this I am sending requests to fetch those types from a different endpoint and I want to include these types of specific pokemon to redux state.
1-redux state without types of pokemons
2-redux state with types of pokemons
My goal is to have a state like in the second picture with types. But when I refresh the page, I only acquire the first picture(actions aren't dispatching). When I change something in my code and save it, I get types as well. I suspect that my problem is in the useEffect, but I couldn't find a solution without creating some nasty loops.
Note: Page parameter in fetchData coming from PokeAPI, it basically returns 15 pokemon for every page.(For now I am just experimenting on the first page)
This is my first question in stackoverflow, I already searched for similar questions but those were dealing with different aspects, so I decided to ask myself.
PokemonList.js --> this is where I need those types
import React, { useEffect } from 'react';
import { ListGroup, ListGroupItem } from "react-bootstrap";
import { useDispatch, useSelector } from 'react-redux';
import _ from "lodash";
import { GetPokemonList, GetSpecificPokemon } from '../redux/actions/PokemonAction';
import { Button } from 'react-bootstrap';
const PokemonList = () => {
const dispatch = useDispatch();
const pokemonList = useSelector(state => state.PokemonList);
useEffect(() => {
const fetchData = (page = 1) => {
dispatch(GetPokemonList(page));
}
fetchData();
}, [dispatch]);
useEffect(() => {
const fetchTypes = () => {
pokemonList.data.forEach(pokemon => {
dispatch(GetSpecificPokemon(pokemon.name));
});
}
fetchTypes();
}, [dispatch]);
const showData = () => {
if (!_.isEmpty(pokemonList.data)) {
return (
<div className="pokemon-list-wrapper">
{pokemonList.data.map((pokemon, index) => {
return (
<div className="pokemon-list-element" key={index}>
<ListGroup>
<ListGroupItem action href={`/pokemon/${pokemon.name}`} variant="success">{pokemon.name}
<Button style={{ float: "right" }}>Test</Button>
</ListGroupItem>
</ListGroup>
</div>
)
})}
</div>
)
}
if (pokemonList.loading) {
return <p>Loading...</p>
}
if (pokemonList.errorMessage !== "") {
return <p>{pokemonList.errorMessage}</p>
}
};
return (
<div>
{showData()}
</div>
)
};
export default PokemonList;
PokemonAction.js
import axios from "axios"
export const GetPokemonList = (page) => async (dispatch) => {
try {
dispatch({
type: "POKEMON_LIST_LOADING"
});
const perPage = 15;
const offset = (page * perPage) - perPage;
const res = await axios.get(`https://pokeapi.co/api/v2/pokemon?limit=${perPage}&offset=${offset}`);
dispatch({
type: "POKEMON_LIST_SUCCESS",
payload: res.data
});
} catch (e) {
dispatch({
type: "POKEMON_LIST_FAIL"
});
}
}
export const GetSpecificPokemon = (name) => async (dispatch) => {
try {
const res = await axios.get(`https://pokeapi.co/api/v2/pokemon/${name}`);
dispatch({
type: "SPECIFIC_POKEMON_SUCCESS",
payload: res.data
});
} catch (e) {
dispatch({
type: "SPECIFIC_POKEMON_FAIL"
});
}
}
PokemonListReducer.js
const initialState = {
data: [],
loading: false,
errorMessage: "",
count: 0
};
const PokemonListReducer = (state = initialState, action) => {
switch (action.type) {
case "POKEMON_LIST_LOADING":
return {
...state,
loading: true,
errorMessage: ""
};
case "POKEMON_LIST_FAIL":
return {
...state,
loading: false,
errorMessage: "unable to get pokemon"
};
case "POKEMON_LIST_SUCCESS":
return {
...state,
loading: false,
data: action.payload.results,
errorMessage: "",
count: action.payload.count
};
case "SPECIFIC_POKEMON_SUCCESS":
const typesMap = action.payload.types.map((type) => {
return type.type.name;
})
return {
...state,
data: state.data.map((pokemon) => pokemon.name === action.payload.name
? {...pokemon, types: typesMap}
: pokemon
),
loading: false,
errorMessage: ""
}
case "SPECIFIC_POKEMON_FAIL":
return {
...state,
loading: false,
errorMessage: "unable to get pokemon"
};
default:
return state
}
}
export default PokemonListReducer;
This is happening because your second useEffect does not wait for your first useEffect to finish and because of that the pokemon list is empty. On code change, since the state already has the pokemon list pre-filled, the second useEffect finds the list and does it's thing. You have to guarantee that the second action is caller right after the first one in order for this to work properly. One way to do this is to dispatch the GetSpecificPokemon action for each pokemon before finishing the GetPokemonList action. Something like this should work:
export const GetPokemonList = (page) => async (dispatch) => {
try {
dispatch({
type: "POKEMON_LIST_LOADING"
});
const perPage = 15;
const offset = (page * perPage) - perPage;
const res = await axios.get(`https://pokeapi.co/api/v2/pokemon?limit=${perPage}&offset=${offset}`);
dispatch({
type: "POKEMON_LIST_SUCCESS",
payload: res.data
});
res.data.result.forEach(pokemon => {
dispatch(GetSpecificPokemon(pokemon.name));
});
} catch (e) {
dispatch({
type: "POKEMON_LIST_FAIL"
});
}
}
Note that you won't be needing the second useEffect if you are doing this. You might also have to change displaying/not displaying the loader part yourself.
Another way is to add pokemonList as the second object in the useEffect's array parameter. I haven't tested it yet but this should work. For example:
useEffect(() => {
const fetchTypes = () => {
pokemonList.data.forEach(pokemon => {
dispatch(GetSpecificPokemon(pokemon.name));
});
}
fetchTypes();
}, [dispatch, pokemonList]);
This will call the useEffect whenever there is a change in pokemonList. In your implementation, useEffect is only called once since the value of dispatch never really changes after that. Adding pokemonList to the array results in the useEffect being called when there is a change in pokemonList also. Use this approach if you want the GetPokemonList action to always be separate from GetSpecificPokemon action i.e there are cases when both are not called together. If both are always called together then the first approach is cleaner.
That being said, these implementations actually result in a lot of network calls. The best way is to avoid the second call if possible (change your UI accordingly?) since you do not have any control over the API. If you do have control over the API you could include the extra data in the first request's response.
Edit: Here is the batch logic
const p = pokemonList.map(({ name }) =>
axios.get(`https://pokeapi.co/api/v2/pokemon/${name}`)
);
const res = await Promise.all(p);
const data = res.map((r) => ({
...r.data,
types: r.data.types.map((type) => type.type.name) // the logic you were using for types
}));
dispatch({
type: "SPECIFIC_POKEMON_SUCCESS",
payload: data
});
And then update the state in the reducer like
case "SPECIFIC_POKEMON_SUCCESS":
return {
...state,
data: action.payload,
loading: false,
errorMessage: ""
};

.map Returning its not function

I have a mern application using redux for state management.
For some reason when I try to map through it, it tells me it's not a function.
It is weird because when I see my props through the console, it shows me it's an array and react knows that I have data in my state. And it also shows the data in my redux dev tools. But when I try to render it gives me that error. Also when i do this.props.products.products it tells me cannot read property of Null.
Here's the github repo
https://github.com/bryanb213/seller
Can anyone explain why
stuff.jsx
import React, { Component } from 'react'
import './stuff.stle.css'
import { getProducts } from '../redux/actions/productActions';
import { connect } from 'react-redux';
class Stuff extends Component {
componentDidMount() {
this.props.getProducts();
}
render() {
console.log('Products from props', this.props)
if (this.props.loading === true) {
return (
<div>Loading...</div >
)
} else {
return(
<div>
{ this.props.products.map(p => (
<h1>{p.name}</h1>
))
}
</div>
)
}
}
}
const mapStateToProps = state => ({
//products from root reducer
products: state.products,
})
export default connect(mapStateToProps, { getProducts })(Stuff);
Action
// Get all products
export const getProducts = () => dispatch => {
axios
.get('http://localhost:5000/api/products/all')
.then(res =>
dispatch({
type: GET_PRODUCTS,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_PRODUCTS,
payload: null
})
);
};
Reducer
import { GET_PRODUCTS } from '../actions/types';
const initialState = {
products: null,
loading: true
}
export default function(state= initialState, action){
switch(action.type){
case GET_PRODUCTS:
console.log('hitting GET_PRODUCTS', action.payload)
return {
...state,
products: action.payload,
loading: false
}
default:
return state
}
}
Server route
router.get('/all', (req, res) => {
Product.find()
.exec()
.then(stuff => {
res.status(200).json(stuff);
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
postman result
render() {
console.log("Products from props", this.props);
const { loading, products } = this.props;
if (loading === true) {
return <div>Loading...</div>;
} else {
return (
<div>{products && products.products.map(p => <h1>{p.name}</h1>)}</div>
);
}
}
this.props.products value is { products : [..] }, so you have to access it by this.props.products.products, in cases like this it will be easier if you use destructring assignment syntax to get the respected values to avoid some confusion.

State returning undefined from Fetch action to Rails backend

I have a Rails backend set up (for an array of ingredients), and it was working previously, however now the fetch action in my Dispatch Action Creator function is returning an undefined state (not retrieving ingredients).
The API endpoints are working perfectly fine (checked through server), however the fetch action is not retrieving the ingredients, and is returning a response.json that is undefined.
I put breakpoints everywhere, to check the state. I have tried to change the contents of the mapStateToProps for the component as well, but the state was undefined before going into the mapState function.
IngredientList component
import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { getIngredients, hideIngredients } from "../actions";
class IngredientList extends React.Component {
render() {
const { ingredients } = this.props;
const ingredientsList = ingredients.map(ingredient => {
return (
<li key={ingredient.id}>
{ingredient.id}. {ingredient.name}
</li>
);
});
return (
<React.Fragment>
<div>
<button
className="button"
onClick={() => this.props.getIngredients()}
>
Get Ingredients
</button>
<button
className="button"
onClick={() => this.props.hideIngredients()}
>
Hide Ingredients
</button>
</div>
<ul>{ingredientsList}</ul>
</React.Fragment>
);
}
}
const structuredSelector = createStructuredSelector({
ingredients: state => state.ingredients
});
const mapDispatchToProps = { getIngredients, hideIngredients };
export default connect(
structuredSelector,
mapDispatchToProps
)(IngredientList);
Actions
export function getIngredientsRequest() {
return {
type: GET_INGREDIENTS_REQUEST
};
}
export function getIngredientsSuccess(json) {
return {
type: GET_INGREDIENTS_SUCCESS,
json
};
}
export function hideIngredients() {
return dispatch => {
dispatch({ type: HIDE_INGREDIENTS });
};
}
export function getIngredients() {
return dispatch => {
dispatch(getIngredientsRequest());
return fetch(`v1/ingredients`)
.then(response => response.json())
.then(json => dispatch(getIngredientsSuccess(json)))
.catch(error => console.log(error));
};
}
Reducers
const initialState = {
ingredients: []
};
function rootReducer(state = initialState, action) {
console.log(action.type);
switch (action.type) {
case "GET_INGREDIENTS_SUCCESS":
console.log(action.json);
return { ...state, ingredients: action.json.ingredients }
case "GET_INGREDIENTS_REQUEST":
console.log('Ingredients request received')
return
case "HIDE_INGREDIENTS":
console.log('Ingredients are being hidden')
return { ...state, ingredients: [] }
case "GET_INGREDIENT_REQUEST":
console.log('One Ingredient request received:', "id:", action.id)
return
case "GET_INGREDIENT_SUCCESS":
console.log('GET_INGREDIENT_SUCCESS')
const ingredients = action.json.ingredients;
const id = action.id;
return {
...state,
ingredients: ingredients.filter(ingredient => ingredient.id.toString() === id)
}
default:
return state
}
}
export default rootReducer;
GET_INGREDIENTS_REQUEST reducers.js:6
Ingredients request received reducers.js:12
This is in the structuredSelector for IngredientList.js:42
Uncaught TypeError: Cannot read property 'ingredients' of undefined
I assume the issue is coming from the reducer below where you are trying to access the action.json.ingredients property here:
switch (action.type) {
case "GET_INGREDIENTS_SUCCESS":
console.log(action.json);
return { ...state, ingredients: action.json.ingredients } // here
In the following code snippet please check how the the response value - variable json - is passed to getIngredientsSuccess function:
export function getIngredients() {
return dispatch => {
dispatch(getIngredientsRequest());
return fetch(`v1/ingredients`)
.then(response => response.json())
.then(json => dispatch(getIngredientsSuccess(json))) // json value
.catch(error => console.log(error));
};
}
My assumption is the value is returned like the following from the API endpoint:
[
{id: 12, name: 'test12'},
{id: 13, name: 'test13'},
]
And not like this:
{
ingredients: [
{id: 12, name: 'test12'},
{id: 13, name: 'test13'},
]
}
So to resolve the issue, you might want to change the following line in the reducer:
switch (action.type) {
case "GET_INGREDIENTS_SUCCESS":
console.log(action.json);
// removed .ingredients
// original value: action.json.ingredients
return { ...state, ingredients: action.json }
I hope this resolves your problem, if not just let me know so we can investigate further.
Additionally it is worth to handle null and undefined values for your variable before calling map function on ingredients variable in rendering as below:
const { ingredients } = this.props;
const ingredientsList = ingredients != null ? ingredients.map(ingredient => {
return (
<li key={ingredient.id}>
{ingredient.id}. {ingredient.name}
</li>);
}) : null;
UPDATE:
Initially your code calls getIngredientsRequest function which goes to the following lines of code where the code does not return the state object:
case "GET_INGREDIENTS_REQUEST":
console.log('Ingredients request received')
return // missing state
So I guess the following correction will do the job here and most likely you won't get the error message further:
case "GET_INGREDIENTS_REQUEST":
console.log('Ingredients request received')
return state;
Let me highlight one important thing about reducer's return statement:
In Redux a reducer takes the starting state and an item to process, and return the new state.
Other important rule is the following:
Always return the state, even if you didn't change it, and even if it's just null. You may not return undefined.
So the reducer should have the new state in the return statement which in this case it was undefined and that caused the error message.
Please read further about reducers here: Actions and reducers: updating state.

Categories

Resources