How can I display this array on the page? - javascript

I am trying to display an array of news articles on the page and getting an error:
Unhandled Rejection (TypeError): this.state.newsPost.map is not a function
and this is my code that i am running:
import React, { Component } from 'react'
import { Container, Row, Col } from 'bootstrap-4-react';
import News from '../Articles/News';
import Post from '../Posts/Post/Post';
import axios from 'axios';
const REACT_APP_NEWS_ARTICLE_API = process.env.REACT_APP_NEWS_ARTICLE_API
export default class Body extends Component {
constructor(props){
super(props)
this.state = {
posts: [{}],
newsPost: [{}]
}
}
componentDidMount = (props) => {
axios.all([axios.get(`${process.env.REACT_APP_SERVER_URL}/posts`),
axios.get(`https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=${REACT_APP_NEWS_ARTICLE_API}`)])
.then(axios.spread((...responses) => {
const responseOne = responses[0]
const responseTwo = responses[1]
this.setState({
posts: responseOne.data,
newsPost: responseTwo.data
})
}
))
};
render() {
console.log(this.state.newsPost) //returns an array of articles
return (
<Container id="bodycontainer" className="container">
<Row className="technews">
// This is the loop that's returning the error
{this.state.newsPost.map((item) => {
<div key={item.id} className="technewsitem">
{item}
</div>
})}
</Row>
</Container>
)
}
}
How can I run the code in order to display the array on the page, any pointers is greatly appreciated.

Check if the value you are assigning to newsPost inside componentDidMount() is an Array
Check data is an Array then use array methods
1
newPost && Array.isArray(newPost) ? newPost.map((value,index)=>{
//code
}):<></>
2
newPost.length>0 ? newPost.map((value,index)=>{
//code
}):<></>

Related

reactjs axios cant get api data

I can't get my api data from https://randomuser.me/api/
But when I'm using another api like http://dummy.restapiexample.com/api/v1/employees it works.
The error:
import React from "react";
import "./App.css";
import Start from "./start";
function App() {
return (
<div className="App">
<Start />
</div>
);
}
export default App;
start.js
import React, { Component } from "react";
import Axios from "axios";
class Start extends Component {
constructor(props) {
super(props);
this.state = {
results: []
};
}
componentDidMount() {
Axios.get("https://randomuser.me/api/").then(res => {
const results = res.data;
this.setState({ results });
console.log(results);
});
}
render() {
return (
<div>
{this.state.results.map(result => {
return <div>{result.id}</div>;
})}
</div>
);
}
}
export default Start;
Problem is that http://dummy.restapiexample.com/api/v1/employees returns array while https://randomuser.me/api/ returns object. Try changing to
componentDidMount() {
Axios.get("https://randomuser.me/api/").then(res => {
const results = res.data.results;
this.setState({ results });
console.log(results);
});
}
You have to use res.data.results. It comes in results object.
Please check your JSON data
last line you missed the "}]" typo error in http://dummy.restapiexample.com/api/v1/employees
componentDidMount() {
Axios.get("http://dummy.restapiexample.com/api/v1/employees").then(res => {
const results = res.data;
this.setState({ results: results });
});
}

Cannot read property 'toLowerCase' of undefined - REACT - FIRESTORE

I'm a bit new to React and Firestore and already trying to figure out what is happening for a couple of hours. I Try to make my filter function working with data which I receive from Firestore in APP.js. I pass the data {tasks, searchTerm} to DASHBOARD component. The filter worked before when using state and props, but after replacing the hard-coded data in state with firestore data, it doesn't work anymore and I get the following error when filtering the array in the DASHBOARD component:
Cannot read property 'toLowerCase' of undefined
I've tried to send the data without any filtering directly to TASKS.js and this is working correctly (all the tasks are shown). But as soon as I pass newArray to , it doesn't work anymore.
Also, when logging task.title in tasks.filter function in the DASHBOARD component, it shows all the data (with a little delay because the data is coming from Firestore)
APP.JS -
import React, { Component } from 'react';
import './App.css';
import Dashboard from './Components/Dashboard/Dashboard'
import AddTask from './Components/Tasks/Task/AddTask'
import Navbar from './Components/Navbar/Navbar'
import Searchbar from './Components/Searchbar/Searchbar'
import firebase from './Firebase';
class App extends Component {
constructor(props) {
super(props)
this.ref = firebase.firestore().collection('tasks')
this.state = {
tasks: [],
searchTerm: ""
}
this.handleLikeButton = this.handleLikeButton.bind(this)
this.handleRemoveButton = this.handleRemoveButton.bind(this)
this.addTask = this.addTask.bind(this)
this.handleFilter = this.handleFilter.bind(this)
}
componentWillMount() {
const db = firebase.firestore()
const allTasks = []
db.collection('tasks').onSnapshot(collection => {
const tasks = collection .docs.map(doc => doc.data())
this.setState({ tasks: tasks, searchTerm: "" })
})
}
handleLikeButton = (task) => (e) => {
const tasks = [...this.state.tasks]
const index = tasks.indexOf(task)
tasks[index].likes++
this.setState({
tasks: tasks
})
}
addTask = (taskName) => (e) => {
this.ref.add({
id: Math.floor(Math.random() * 100000000000000),
title: taskName,
likes: 0
})
}
handleRemoveButton = (removingTask) => (e) => {
const tasks = [...this.state.tasks]
const newTasks = tasks.filter(task => removingTask.id !== task.id)
this.setState({
tasks: newTasks
})
}
handleFilter = (searchTerm) => {
this.setState({
searchTerm: searchTerm
})
}
render() {
return (
<div className="App">
<Navbar />
<Searchbar handleFilter={this.handleFilter} />
<AddTask addTask={this.addTask} />
<Dashboard tasks={this.state.tasks} searchTerm={this.state.searchTerm} handleLikeButton={this.handleLikeButton} handleRemoveButton={this.handleRemoveButton}/>
</div>
);
}
}
export default App;
DASHBOARD.JS -
import React, { Component } from 'react'
import Tasks from '../Tasks/Tasks'
class Dashboard extends Component {
constructor(props) {
super(props)
this.filterTasks = this.filterTasks.bind(this)
}
filterTasks = () => {
const tasks = [...this.props.tasks]
const newArray = tasks.filter(task =>
task.title.toLowerCase().indexOf(this.props.searchTerm.toLowerCase()) > -1)
return (
<Tasks tasks={newArray} handleLikeButton={this.props.handleLikeButton} handleRemoveButton={this.props.handleRemoveButton} />
)
}
render() {
return (
<div>
<h2>Dashboard</h2>
{this.filterTasks()}
</div>
)
}
}
export default Dashboard
ADDTASK.JS
import React, { Component } from 'react'
class AddTask extends Component {
constructor(props) {
super(props)
this.state = {
addNewTaskFieldEmpty: true,
taskName: ""
}
this.onChangeHandler = this.onChangeHandler.bind(this)
this.disableButton = this.disableButton.bind(this)
}
onChangeHandler(e) {
this.setState({
taskName: e.target.value,
})
this.disableButton(e.target.value)
}
disableButton(taskName) {
if(taskName.length == 0) {
this.setState({addNewTaskFieldEmpty: true})
} else {
this.setState({addNewTaskFieldEmpty: false})
}
}
render() {
return (
<div>
<div className="mdc-text-field half-size">
<input className="mdc-text-field__input " onChange={this.onChangeHandler} />
<div className="mdc-line-ripple"></div>
<label className="mdc-floating-label">Task Name</label>
</div>
<a className={"btn-floating btn-large waves-effect waves-light red " + (this.state.addNewTaskFieldEmpty ? 'disabled' : '')} onClick={this.props.addTask(this.state.taskName)}><i className="material-icons">add</i></a>
</div>
)
}
}
export default AddTask
Lint your App.css for any errors.
I encountered this message. I traced it to a CSS include:
.box-table { border-color:; border: 1px solid #dbdad8; }
The missing value of border-color: caused npm run build to fail.
Interestingly, the same file contained
.submenu-button.submenu-opened:after { background:; }
which caused no problems at all.

Component not displaying using consumer

I correctly passed the data to the consumer using context api,
however the product component doesn't display.
Listofproducts component:
import React, { Component } from "react";
import Product from "./product";
import { Consumer } from "./context";
class Listofproducts extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<Consumer>
{value => {
value.map(data => {
console.log(data); // its returning the data correctly
return <Product key={data.id} product={data} />;
});
}}
</Consumer>
);
}
}
export default Listofproducts;
Product component where i sent the data with the consumer value:
import React, { Component } from "react";
class Product extends Component {
render() {
console.log(this.props.product); // not showing anything on the console nor an error
return <div>hello from product</div>;
}
}
export default Product;
Thank you in advance.
You aren't returning the mapped data from within the Consumer which is why your Product components are not getting rendered. Add a return keyword to mapped data and it will work correctly
<Consumer>
{value => {
return value.map(data => {
console.log(data);
return <Product key={data.id} product={data} />;
});
}}
</Consumer>
You are not returning anything from the function given as child to Consumer.
Add the return keyword and it will work as expected.
class Listofproducts extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<Consumer>
{value => {
return value.map(data => {
console.log(data); // its returning the data correctly
return <Product key={data.id} product={data} />;
});
}}
</Consumer>
);
}
}

React Redux: Works Perfect with Sample A Code but returns dispatch error with sample B Code

React Redux: Works Perfect with Sample A Code but returns dispatch error with sample B Code
Am trying to display Post records and then send/post back the records to server backend via onclick function.
To this effect, I have created two samples of Codes A & B.
Sample A codes works perfects but sample B Code works partly.
In sample code A below. Everything works fine as expected as I can display records and post back to server backend.
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { Actions } from '../_actions';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.props.dispatch(userActions.getRec());
}
handlePostId(postid,post_content){
//return (e) => this.props.dispatch(Actions.sendData(postid,post_content));
return this.props.dispatch(Actions.sendData(postid,post_content));
}
render() {
const { post1, posts1} = this.props;
return (
<div>
{posts1.items1 &&
<ul>
{posts1.items1.map((post1, index1) =>
<li key={post1.id}>
{post1.content} ({post1.id})
<input type="button" value="Send Data Working" onClick={() => this.handlePostId(post1.id, 55)} />
</li>
)}
</ul>
}
</div>
);
}
}
function mapStateToProps(state) {
const { posts1, post1} = state;
return {
post1,
posts1,
};
}
const connectedApp = connect(mapStateToProps)(App);
export { connectedApp as App }
Sample B Code
Here is my requirements and my issue with Sample Code B.
I have a requirements to create a Props and have records returns in it as per code below
const RenderPost = (props) => {
return (<React.Fragment><li >
{props.post1.id} - {props.post1.content}
<input type="button" value="Send Data not Working" onClick={() => props.handlePostId(props.post1.id, 55)} />
</li>
</React.Fragment>
);
};
In the Map function, I rendered Post records as follows
<RenderPost post1={post1} key={i} handlePostId={this.handlePostId} />
Sample B partly works as it displays record very well but my issue is that If I click on Send Data button so as to
post records to server backend, it will display error
Cannot read property 'dispatch' of undefined
at Object.handlePostId (bundle.js:73753)
at onClick.
Is props conflicting or what?.
Here is sample B code
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { Actions } from '../_actions';
const RenderPost = (props) => {
return (<React.Fragment><li >
{props.post1.id} - {props.post1.content}
<input type="button" value="Send Data not Working" onClick={() => props.handlePostId(props.post1.id, 55)} />
</li>
</React.Fragment>
);
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.props.dispatch(Actions.getRec());
}
handlePostId(postid,post_content) {
//return (e) => this.props.dispatch(Actions.sendData(postid,post_content));
return this.props.dispatch(Actions.sendData(postid,post_content));
}
render() {
const { post1, posts1} = this.props;
return (
<div>
{posts1.items1 &&
<ul>
{posts1.items1.map((post1, i) =>
<RenderPost post1={post1} key={i} handlePostId={this.handlePostId} />
)}
</ul>
}
</div>
);
}
}
function mapStateToProps(state) {
const { posts1, post1} = state;
return {
post1,
posts1,
};
}
const connectedApp = connect(mapStateToProps)(App);
export { connectedApp as App };
You have to bind the handlePostId function to the component
<RenderPost post1={post1} key={i} handlePostId={this.handlePostId.bind(this)} />
Looks like you didn't bind handlePostId in your constructor.
Alternatively, you could do this without needing to bind.
handlePostId = (postId, postContent) => {
return this.props.dispatch(Actions.sendData(postId, postContent));
}
Then call it like you did before:
<RenderPost post1={post1} key={i} handlePostId={this.handlePostId} />

React TypeError: Cannot read property 'map' of undefined on passing props

After get the comments array from post component and pass it to comments component
the logs start to show the error in the screenshot below
the components are:
import React, { Component } from "react";
import axios from "axios";
import Comments from "../components/comments";
class Article extends Component {
constructor(props) {
super(props);
this.state = {
title: "",
error: "",
comment: ""
};
}
componentDidMount() {
this.getComments();
}
getComments = () => {
const {
match: { params }
} = this.props;
return axios
.get(`/articles/${params.id}/comments`, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
}
})
.then(response => {
return response.json();
})
.then(response => this.setState({ comments: response.comments }))
.catch(error =>
this.setState({
error
})
);
};
render() {
return (
<div>
{this.state.title}
<div>
<h2>Comments</h2>
<Comments
getComments={this.getComments}
/>
</div>
</div>
);
}
}
export default Article;
and Comments component
import React, { Component } from "react";
import PropTypes from "prop-types";
import Comment from "./comment";
import axios from "axios";
import Article from "../screens/article";
class Comments extends Component {
constructor(props) {
super(props);
this.state = {
comments: [],
comment: "",
error: ""
};
this.load = this.load.bind(this);
this.comment = this.comment.bind(this);
}
componentDidMount() {
this.load();
}
load() {
return this.props.getComments().then(comments => {
this.setState({ comments });
return comments;
});
}
comment() {
return this.props.submitComment().then(comment => {
this.setState({ comment }).then(this.load);
});
}
render() {
const { comments } = this.state;
return (
<div>
{comments.map(comment => (
<Comment key={comment.id} commment={comment} />
))}
</div>
);
}
}
export default Comments;
so, I've tried to pass it by props, and set the state on comments component.
and instead of use just comments.map I've tried to use this.state but show the same error in the logs.
So, someone please would like to clarify this kind of issue?
seems pretty usual issue when working with react.
If an error occurs you do:
.catch(error => this.setState({ error }) );
which makes the chained promise resolve to undefined and that is used as comments in the Comments state. So you have to return an array from the catch:
.catch(error => {
this.setState({ error });
return [];
});
Additionally it woupd make sense to not render the Comments child at all if the parents state contains an error.
The other way is checking whether it’s an array and if so check it’s length and then do .map. You have initialized comments to empty array so we don’t need to check whether it’s an array but to be on safer side if api response receives an object then it will set object to comments so in that case comments.length won’t work so it’s good to check whether it’s an array or not.
Below change would work
<div>
{Array.isArray(comments) && comments.length>0 && comments.map(comment => (
<Comment key={comment.id} commment={comment} />
))}
</div>
The first time the comments component renders there was no response yet so comments were undefined.
import React, { Component } from "react";
import PropTypes from "prop-types";
import Comment from "./comment";
import axios from "axios";
import Article from "../screens/article";
class Comments extends Component {
constructor(props) {
super(props);
this.state = {
comments: [],
comment: "",
error: ""
};
this.load = this.load.bind(this);
this.comment = this.comment.bind(this);
}
componentDidMount() {
this.load();
}
load() {
return this.props.getComments().then(comments => {
this.setState({ comments });
return comments;
});
}
comment() {
return this.props.submitComment().then(comment => {
this.setState({ comment }).then(this.load);
});
}
render() {
const { comments } = this.state;
if (!comments) return <p>No comments Available</p>;
return (
<div>
{comments.map(comment => (
<Comment key={comment.id} commment={comment} />
))}
</div>
);
}
}
export default Comments;

Categories