ReactJS looping through set - javascript

I've been strugling with this error:
Uncaught TypeError: data.map is not a function
Here's my code:
import React from 'react';
import PropTypes from 'prop-types';
const Foo = ( props ) => {
const data = props.data;
return (
<div>
{
!data ? null : (
data.map((item, index) =>
<a>{item.name}</a>)
)
}
</div>
)
};
export default foo;
What i pass to Foo is a Set<> of these:
public class Bar extends Dto {
public BigDecimal id;
public String name;
}
Any ideas of what might be the case here?
EDIT:
import React, { Component } from 'react';
class AnotherFoo extends Component {
render () {
const data = this.props;
return (
<div>
<Foo data={data.resultSet} />
</div>
);
}
}

I'm guessing your resultSet is null or undefined at some point. One thing you can do to add some robustness and clarity is to add propTypes and defaultProps to your component
import React from 'react';
import PropTypes from 'prop-types';
const Foo = ( props ) => {
const data = props.data;
return (
<div>
{
!data ? null : (
data.map((item, index) =>
<a>{item.name}</a>)
)
}
</div>
);
};
Foo.propTypes = {
data: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string
})
};
Foo.defaultProps = {
data: []
};
export default Foo;
This will do a couple things. Give you some warnings when data is the wrong type and or if the items in data are the wrong shape (They should be objects with a name property). Also... it will give you an empty array if data is undefined. This should shed some light on your issue.

Related

Passing data from Main layout to subpages in Nextjs

I'm trying to do something like this;
I have a file called /components/master_layout.js and it has the following content:
import useUser from "../data/use-user";
function MasterLayout({ children }) {
const { data, error, mutate } = useUser();
if ( error ) return <div>error</div>
if ( !data && !error ) return <div>loading..</div>
return (
<div>
{children}
</div>
)
}
export default MasterLayout
In short, this layout file returns according to the response of the useuser function.
Here is an example of a page where I use this layout:
file path and name: /pages/dashboard/index.js
import MasterLayout from "../../components/master_layout";
function Dashboard() {
return (
<MasterLayout>
dashboard..
</MasterLayout>
)
}
export default Dashboard
Can I use useUser data from Layout in '/pages/dashboard/index.js' and my other pages?
The reason I want this is, I'm trying to do something like:
import MasterLayout from "../../components/master_layout";
function Dashboard({data}) {
return (
<MasterLayout>
Welcome back, {data.username}
</MasterLayout>
)
}
export default Dashboard
Do I have any other choice but to pull the useUser for each page one by one and transfer it to the master layout as
You can use HOC pattern in this case. Something like
// with-data.js
import React from "react";
import useUser from "../data/use-user";
const withData = (WrappedComponent) => {
class WithData extends React.Component {
constructor(props) {
super(props);
this.state = {
data: "",
};
}
componentDidMount() {
const { data, error, mutate } = useUser();
this.setState({data:data});
}
render() {
const { data, ...otherProps } = this.props;
return (
<WrappedComponent data={this.state.data}/>
)
//* See how we can enhance the functionality of the wrapped component
}
}
return WithData;
};
export default withData;
Now you can use the withData,
import MasterLayout from "../../components/master_layout";
import withData from "../withData.js"
function Dashboard({data}) {
return (
<MasterLayout>
Welcome back, {data.username}
</MasterLayout>
)
}
export default withData(Dashboard);
In fact wrapping around any component with withData, can access the data variable.

How can I display this array on the page?

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
}):<></>

Props in other component is undefined React

I'm newbie in React and trying to build a sample search filter with data from API. Unfortunately I have problem with this code.
It's get me an error ,,Cannot read property 'filter' of undefined".
It seems to me like child component doesn't get props from parent but I declared and imported this in code.
I've tried everything what I found on the internet but nothing helps. Can someone help me out with understanding what I made wrong?
Child
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Data from './Data';
class App extends Component {
constructor() {
super();
this.state = {
search : " "
};
}
updatedSearch(event) {
this.setState(
{search : event.target.value.substr(0,15)}
)
}
render () {
console.log(this.props.names)
let filterednames = this.props.names.filter(
(name) => {
return name.toLowerCase().indexOf(this.state.
search.toLowerCase()) !== -1;
}
);
return (
<div className = "App">
<h1> Users list </h1>
<Data />
<input type = "text"
placeholder = "Search by user name"
value = {this.state.search}
onChange = {this.updatedSearch.bind(this)}
/>
<ol>
{filterednames.map(name => (
<li key={name}>{name}</li>
))}
</ol>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('root'));
export default App;
Parent
import React, { Component } from 'react';
import App from './index';
class Data extends Component {
constructor(props) {
super(props);
this.state = {
names : [],
}
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
//Response
.then(response => response.json())
.then(output => {
let data = output;
//names in array
let listaimion = [];
for (let index = 0; index < data.length; index++) {
listaimion.push(data[index].name)
}
this.setState({names : listaimion})
})
}
render () {
return (
<div className = "Data">
<App names = {this.state.names} />
</div>
)
}
}
export default Data;
In the parent component, App needs to be declared. Also, App looks like your entry point of your application. Seems like, you might have mixed up Child and Parent here.
Parent -
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Data from './Data';
class App extends Component() {
constructor() {
this.state = {
names : [],
}
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
//Response
.then(response => response.json())
.then(output => {
let data = output;
let listaimion = [];
for (let index = 0; index < data.length; index++) {
listaimion.push(data[index].name)
}
this.setState({names : listaimion});
});
}
render () {
return (
<div className = "Data">
<Data names = {this.state.names} />
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('root'));
export default App;
Child
import React, { Component } from 'react';
class Data extends Component {
constructor(props) {
super(props);
}
render() {
let filterednames = this.props.names.filter((name) => {
return name.toLowerCase().indexOf(this.state.
search.toLowerCase()) !== -1;
}
);
return (<div>{filterednames.join(',')}</div>)
}
}
The <App> component should be the parent - that is where your state should live. You would then pass this.state.names from <App> to <Data> inside the App render method. You should not import App inside Data - App should render Data.
// App.js
class App extends Component {
state = {
names: []
}
componentDidMount(){
// fetch data and when it's done use this.setState({ names: data })
}
render() {
return <Data names={this.state.names}/>
}
}
// Data.js
const Data = (props) => {
return props.names.map(() => {...your map function})
}

getting state from connect(mapStateToProps) > UNDEFINED

I am kind of struggling to get the state of book, when I log the props I get book: undefined.
Any tips?
import React from 'react';
import { connect } from 'react-redux';
import BookForm from './BookForm';
const EditBook = (props) => {
console.log(props)
return (
<div>
hello
</div>
);
};
const mapStateToProps = (state, props) => {
return {
book: state.books.find((book) => book.id === props.match.params.id)
};
};
export default connect(mapStateToProps)(EditBook);
Rest of the project is on my Github: https://github.com/bananafreak/bookshelf
Update the Link in the BookListItem. You don't need the : before ${id}. The : is causing the problem.
<Link to={`/edit/${id}`}><h2>{title}</h2></Link>
In the EditBook component return the following
<BookForm {...props}></BookForm>
In the BookForm constructor set the state from props.book
this.state = { ...props.book }
I've ran into issues with this before where === will fail because the types of book.id and props.match.params.id are different. The params values are always strings - maybe try parseInt(props.match.params.id) or == comparison (with type coercion).
I managed to found where was my mistake.
In the component BookListItem:
import React from 'react';
import { Link } from 'react-router-dom';
const BookListItem = ({ author, title, genre, text, id, pages }) => (
<div>
<Link to={`/edit/${id}`}><h2>{title}</h2></Link>
<h3>by: {author}</h3>
<p>{genre}</p>
{pages > 0 && <p>{pages} pages</p>}
<p>{text}</p>
<p>-------------------------------</p>
</div>
);
export default BookListItem;
Before the ${id} I had unfortunately colon {/edit/:${id}} so then book.id and props.match.params.id could not match

Uncaught error returning Array with map()

I'm building a search engine with React.js, where I can look for GIPHY gifs, using their API. When I type a word in the search bar, I get this error: Uncaught (in promise) TypeError: props.gifs.map is not a function
at GifList (SelectedList.js:19)
The console log returns an array, tough :
import React from 'react';
import GifItem from './SelectedListItem';
const GifList = (props) => {
console.log(props.gifs); // Logs Array in the console
const gifItems = props.gifs.map((image) => { // <=======
return <GifItem key={image.id} gif={image} />
});
return (
<div className="gif-list">{gifItems}</div>
);
};
export default GifList;
How is fetching the gifs:
import React from 'react'; //react library
import ReactDOM from 'react-dom'; //react DOM - to manipulate elements
import './index.css';
import SearchBar from './components/Search';
import GifList from './components/SelectedList';
class Root extends React.Component { //Component that will serve as the parent for the rest of the application.
constructor() {
super();
this.state = {
gifs: []
}
this.handleTermChange = this.handleTermChange.bind(this)
}
handleTermChange(term) {
console.log(term);
let url = 'http://api.giphy.com/v1/gifs/search?q=${term.replace(/\s/g, '+')}&api_key=dc6zaTOxFJmzC';
fetch(url).
then(response => response.json()).then((gifs) => {
console.log(gifs);
console.log(gifs.length);
this.setState({
gifs: gifs
});
});
};
render() {
return (
<div>
<SearchBar onTermChange={this.handleTermChange} />
<GifList gifs={this.state.gifs} />
</div>
);
}
}
ReactDOM.render( <Root />, document.getElementById('root'));
Any help is appreciated! Thanks! :)
As per your comment, props.gifs is an object and props.gifs.data is an array. So you need to write
const gifItems = props.gifs && props.gifs.data && props.gifs.data.map((image) => {
return <GifItem key={image.id} gif={image} />
});

Categories