I am trying to display recipes and not sure if I have this setup correctly. I am pulling recipes from a rails api via get fetch request. At the moment nothing is displaying.
Here is my recipe container:
import React, { Component } from 'react'
import RecipeList from '../components/RecipeList'
import RecipeInput from '../components/RecipeInput'
import { connect } from 'react-redux'
import { postRecipes } from '../actions/postRecipes.js'
import { getRecipes } from '../actions/getRecipes'
class RecipeContainer extends Component{
constructor(props){
super(props)
}
componentDidMount(){
getRecipes()
}
render(){
return (
<div>
<RecipeInput postRecipes={this.props.postRecipes} />
<RecipeList getRecipes={this.props.recipes} />
</div>
)
}
}
const mapStateToProps = state =>({
recipes: state.recipes
})
const mapDispatchToProps = dispatch =>{
return{
postRecipes: (recipe) => dispatch(postRecipes(recipe)),
getRecipes: () => dispatch(getRecipes())
// deleteRecipe: id => dispatch({type: 'Delete_Recipe', id})
}
}
export default connect(mapStateToProps,mapDispatchToProps)(RecipeContainer)
Here is my get request....notice that I am returning my Recipe component here.
export const getRecipes = () => {
const BASE_URL = `http://localhost:10524`
const RECIPES_URL =`${BASE_URL}/recipes`
return (dispatch) => {
dispatch({ type: 'START_FETCHING_RECIPES_REQUEST' });
fetch(RECIPES_URL)
.then(response =>{ return response.json()})
.then(recipes => dispatch({ type: 'Get_Recipes', recipes }));
};
}
This is where I am trying to render the Recipe component from the get request
import React, {Component} from 'react';
// import { getRecipes } from '../actions/getRecipes.js';
import Recipe from './Recipe.js'
class RecipeList extends Component {
// componentDidMount(){
// getRecipes()
// }
render() {
return (
<div>
{this.props.recipes.map(recipe => (<Recipe recipe={recipe} key={recipe.id} />))}
</div>
)
}
}
export default RecipeList;
Edit: Added reducer
switch(action.type){
case 'Add_Recipe':
const recipe = {
name: action.name,
ingredients: action.ingredients,
chef_name: action.chef_name,
origin: action.origin,
category: action.category
}
return{
...state,
recipes: [...state.recipes, recipe],
}
case 'START_FETCHING_RECIPES_REQUEST':
return {
...state,
recipes: [...state.recipes],
requesting: true
}
case 'Get_Recipes':
return {
...state, recipes: action.recipes,
requesting: false
}
default:
return state
}
}
How can I correct this to make it work?
Issue
You are not passing the recipes to the RecipeList component that were fetched and presumably stored in state, and fed back to the UI via RecipeContainer.
Solution
Pass the recipe state from RecipeContainer to RecipeList as a prop. and then render/map the recipes from props.
RecipeContainer
class RecipeContainer extends Component{
componentDidMount() {
getRecipes();
}
render() {
return (
<div>
<RecipeInput postRecipes={this.props.postRecipes} />
<RecipeList getRecipes={this.props.recipes} /> // <-- pass recipe state
</div>
)
}
}
const mapStateToProps = state => ({
recipes: state.recipes,
});
const mapDispatchToProps = dispatch => {
return {
postRecipes: (recipe) => dispatch(postRecipes(recipe)),
getRecipes: () => dispatch(getRecipes())
}
};
RecipeList
class RecipeList extends Component {
render() {
const { recipes } = this.props;
return (
<div>
{recipes.map(recipe => (
<Recipe recipe={recipe} key={recipe.id} />
))}
</div>
);
}
}
The actual solution to this was I needed to have an explicit return in my mapStateToProp function.
Eg.
const mapStateToProp = state =>{
return {
recipes: state.recipes
}
}
Related
I use the API to get Quotes, but I get an Unhandled Rejection error "(TypeError): this.props.message.map is not a function" https://ibb.co/dWqhjXK, I used debugger checked props saw that I get answers https://ibb.co/wM3mLb9 what then could be the reason? why am i getting this error? here is my code
Message.jsx
import React from 'react';
export class Message extends React.Component {
render() {
const list = this.props.message.map((item, index) => {
return <div key={index}>
<p>{item.content}</p>
</div>
});
return (
<div>
<p>{list}</p>
</div>
);
}
}
MessageContainer.js
import React from 'react';
import {connect} from "react-redux";
import {Message} from "./Message";
import {getMessageThunk} from "../../redux/message-reducer";
class MessageContainer extends React.Component {
componentDidMount() {
this.props.getMessageThunk();
}
render() {
return <Message {...this.props} />
}
}
const mapStateToProps = (state) => ({
message: state.message.users
})
export default connect(mapStateToProps, {getMessageThunk})(MessageContainer);
message-reducer.js
import {messageAPI} from "../Api/Api";
const MESSAGE = 'MESSAGE';
let initialState = {
users: [],
};
export const messageReducer = (state = initialState, action) => {
switch (action.type) {
case MESSAGE: {
return {...state, users: action.users}
}
default:
return state;
}
}
export const messageCreator = (users) => {
return {
type: MESSAGE, users
}
};
export const getMessageThunk = (users) => (dispatch) => {
messageAPI.getMessageAPI(users).then(response => {
dispatch(messageCreator(response.data));
})
}
Api.js
import * as axios from "axios";
const instance = axios.create({
withCredentials: true,
url: 'https://quotes15.p.rapidapi.com/quotes/random/',
headers: {
'x-rapidapi-key': 'bf490d72a0msh3bf159a87e0c27fp107a51jsn062ca1b9b00e',
'x-rapidapi-host': 'quotes15.p.rapidapi.com'
}
});
export const messageAPI = {
getMessageAPI() {
return instance.get(`https://quotes15.p.rapidapi.com/quotes/random/`)
},
};
Your message is not an array but an object. You do not have anything to iterate over there you could either simply return this.props.message.content but this is not a list.
const item = <p>{this.props.message.content}</p>
I am trying to display messages on the screen which I receive from api. I checked in the debugger (here are the screenshots https://ibb.co/gShTG8g https://ibb.co/dQmfwJp) where all the stages are going fine, but in the end I get an error called TypeError: this.props.message.map is not a function. Here is my actual code, link to api https://rapidapi.com/ajith/api/messages
Messages.jsx
import React from "react";
export class Messages extends React.Component {
render() {
const MessageList = this.props.message.map((item, index) => {
return <div key={index}>
<p>{item.Message}</p>
</div>
});
return(
<div>
{MessageList}
</div>
);
}
}
MessagesContainer.js
import React from 'react';
import { connect } from 'react-redux';
import {Messages} from "./Messages";
import {getMessagesThunk} from "../../Redux/users-reducer";
class MessagesContainer extends React.Component {
componentDidMount() {
this.props.getMessagesThunk();
}
render() {
return(
<>
<Messages {...this.props} />
</>
)
}
}
let mapStateToProps = (state) => ({
message: state.usersPage.messages
})
export default connect(mapStateToProps, {getMessagesThunk})(MessagesContainer);
users-reducer.js (Here is a part of my code)
let initialState = {
messages: [],
};
case MESSAGE:
return {
...state, messages: action.messages
}
export const getMessage = (messages) => ({type: MESSAGE, messages})
export const getMessagesThunk = (messages) => {
return (dispatch) => {
usersAPI.message(messages).then(response => {
if(response.data) {
dispatch(getMessage(response.data.Message))
}
})
}
}
Api.js
import axios from "axios";
const instance = axios.create({
params: {category: 'love'},
withCredentials: true,
headers: {
"API-KEY": "6bec01a1-e00c-42ca-ab9d-a03ad2e730cc",
'x-rapidapi-key': 'bf490d72a0msh3bf159a87e0c27fp107a51jsn062ca1b9b00e',
'x-rapidapi-host': 'ajith-messages.p.rapidapi.com'
}
})
export const usersAPI = {
message() {
return instance.get(`https://ajith-messages.p.rapidapi.com/getMsgs`)
},
}
This is because the message is not an array you can loop through. It's a simple string.
I get the pathname in the WorldPage component and pass this value to the context.jsx in which I want to request data using the pathname.
However, I cannot get the correct value in the componentDidMount() method.
console.log(this.state.tab) should be /world, but still /home.
import axios from "axios";
export const Context = React.createContext();
export class Provider extends Component {
state = {
news_list: [],
tab: "/home",
tabChange: (tabName) => {
if (this.state.tab !== tabName) {
this.setState({
tab: tabName,
});
}
},
};
componentDidMount() {
console.log(this.state.tab);
axios
.get(this.state.tab)
.then((res) => {
console.log(res.data);
this.setState({
news_list: res.data,
});
// console.log(this.state.news_list);
})
.catch((err) => console.log(err));
}
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
);
}
}
export const Consumer = Context.Consumer;
import React, { Component } from "react";
import News from "../News/News";
import { Consumer } from "../../context";
export default class WorldPage extends Component {
render() {
const tabName = window.location.pathname;
return (
<Consumer>
{(value) => {
const { tabChange } = value;
tabChange(tabName);
console.log(tabName);
return (
<React.Fragment>
<News />
</React.Fragment>
);
}}
</Consumer>
);
}
}
I'm on my Home Component where I need to show the article feed and for that, I have to have the articleList array. But for some reason when I look into the store, articleList is null. Also, the console.log that I have placed after fetching the data is also not working. It all seems strange.
Home.js
import React, { Component } from "react"
import { connect } from "react-redux"
import { listAllArticles } from "../actions/articles"
class Home extends Component {
componentDidMount() {
this.props.dispatch(listAllArticles)
}
render() {
console.log(this.props)
return (
<div style={{ textAlign: "center" }}>
<h1>Conduit</h1>
<h5>A place to share your knowledge</h5>
</div>
)
}
}
const mapStateToProps = (state) => {
return state
}
export default connect(mapStateToProps)(Home)
listAllArticles
export const listAllArticles = () => {
console.log("inside listAllArticles action creator")
return dispatch => {
fetch("https://conduit.productionready.io/api/articles")
.then(res => res.json())
.then(data => {
console.log(data.articles)
dispatch({
type: "LIST_ALL_ARTICLES",
data: data.articles
})
})
}
}
articleReducer
const initState = {
articleList: null
}
export const articleReducer = (state=initState, action) => {
console.log("inside article reducer")
switch(action.type) {
case "LIST_ALL_ARTICLES":
return {...state, articleList: action.data}
default:
return state
}
}
I want to list posts in PostList.js component from JSON file
I use react-redux for state managment and redux-saga to get json file
My components are Post.js and PostList.js:
Post.js
const Post = ({ post }) => {
<li>
{post}
</li>
}
export default Post
PostList.js
class PostList extends React.Component {
componentDidMount() {
console.log('did mount');
this.props.fetchPosts();
}
render() {
return (
<div>
<ul>
{this.state.posts(post => (
<Post key={post.id} {...post} />
))}
</ul>
</div>
)
}
}
export default PostList
Reducer
export default (state = [], action) => {
switch (action.type) {
case "FETCH_POSTS":
return {
...state,
loading: true,
posts: []
}
case "FETCH_FAILD":
return {
...state,
loading: false,
posts: []
}
case "FETCH_SUCCESS":
return Object.assign({}, state, {
posts: action.posts
})
default:
return state;
}
}
Actions.js
export const fetchPosts = () => {
return {
type: 'FETCH_POSTS'
}
}
export const fetchSuccess = data => ({
type: "FETCH_SUCCESS",
posts: data
})
export const fetchFaild = () => {
return {
type: 'FETCH_FAILD'
}
}
GetPosts.js (Container)
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import PostList from '../components/PostList'
import { fetchPosts } from '../actions'
const mapStateToProps = state => ({ posts: state.posts });
const mapDispatchToProps = dispatch => bindActionCreators({fetchPosts}, dispatch);
const GetPosts = connect(
mapStateToProps,
mapDispatchToProps
)(PostList)
export default GetPosts
Saga.js
export function* fetchProducts() {
try {
console.log('saga')
const posts = yield call(api_fetchPost);
yield put({ type: "FETCH_SUCCESS", posts});
} catch (e) {
yield put({ type: "FETCH_FAILD", e});
return;
}
}
export function* watchFetchProducts() {
yield takeEvery("FETCH_POSTS", fetchProducts)
}
You are fetching posts from the state of your postlist component. Redux mapStateToProps map the redux state to connected component's props and not state
class PostList extends React.Component {
componentDidMount() {
console.log('did mount');
this.props.fetchPosts();
}
render() {
return (
<div>
<ul>
{this.props.posts && this.props.posts.map(post => {
return ( <Post key={post.id} {...post} /> );
})}
</ul>
</div>
)
}
}
export default PostList
Change this.state.posts to this.props.posts