Cannot read length property of undefined - reactjs

i am trying to render the length of the state to the screen and it keeps telling me, cannot read length property of undefined. When i tried logging the value of state to the console, at first it was undefined, after running a search the value updated and there were 5 items in the array. problem now is when it updates, it does not update on the screen, either that or it flat out crashes.
export default class App extends Component {
state={
vidoes:[]
};
onTermSubmit = async term =>{
const response = await youtube.get('/search',{
params:{
q:term,
}
});
this.setState({videos:response.data.items})
};
render() {
console.log(this.state.videos)
return (
<div className="ui container">
<SearchBar onFormSubmit={this.onTermSubmit}/>
{this.state.vidoes.length}
</div>
)
}
The ultimate goal is to have the length of state render on the screen and now that it is being described as undefined, it will be impossible to loop throght it.

There are some typo videos. Please change it to this.state.videos.length in render method.
render() {
console.log(this.state.videos)
return (
<div className="ui container">
<SearchBar onFormSubmit={this.onTermSubmit}/>
{this.state.videos.length}
</div>
)
}
Hope this will work for you.

Try This,
export default class App extends Component {
state={
videos:[]
};
onTermSubmit = async term =>{
const response = await youtube.get('/search',{
params:{
q:term,
}
});
this.setState({videos:response.data.items})
};
render() {
console.log(this.state.videos)
return (
<div className="ui container">
<SearchBar onFormSubmit={this.onTermSubmit}/>
{this.state.videos.length}
</div>
)
}

Related

react recreating a component when I don't want to

I'm super new to react, this is probably a terrible question but I'm unable to google the answer correctly.
I have a component (CogSelector) that renders the following
import React from "react"
import PropTypes from "prop-types"
import Collapsible from 'react-collapsible'
import Cog from './cog.js'
const autoBind = require("auto-bind")
import isResultOk from "./is-result-ok.js"
class CogSelector extends React.Component {
constructor(props) {
super(props)
this.state = {
docs: null,
loaded: false,
error: null
}
autoBind(this)
}
static get propTypes() {
return {
selectCog: PropTypes.func
}
}
shouldComponentUpdate(nextProps, nextState){
if (nextState.loaded === this.state.loaded){
return false;
} else {
return true;
}
}
componentDidMount() {
fetch("/api/docs")
.then(isResultOk)
.then(res => res.json())
.then(res => {
this.setState({docs: res.docs, loaded: true})
}, error => {
this.setState({loaded: true, error: JSON.parse(error.message)})
})
}
render() {
const { docs, loaded, error } = this.state
const { selectCog } = this.props
if(!loaded) {
return (
<div>Loading. Please wait...</div>
)
}
if(error) {
console.log(error)
return (
<div>Something broke</div>
)
}
return (
<>
Cogs:
<ul>
{docs.map((cog,index) => {
return (
<li key={index}>
<Cog name={cog.name} documentation={cog.documentation} commands={cog.commands} selectDoc={selectCog} onTriggerOpening={() => selectCog(cog)}></Cog>
</li>
// <li><Collapsible onTriggerOpening={() => selectCog(cog)} onTriggerClosing={() => selectCog(null)} trigger={cog.name}>
// {cog.documentation}
// </Collapsible>
// </li>
)
})}
{/* {docs.map((cog, index) => { */}
{/* return ( */}
{/* <li key={index}><a onClick={() => selectCog(cog)}>{cog.name}</a></li>
)
// })} */}
</ul>
</>
)
}
}
export default CogSelector
the collapsible begins to open on clicking, then it calls the selectCog function which tells it's parent that a cog has been selected, which causes the parent to rerender which causes the following code to run
class DocumentDisplayer extends React.Component{
constructor(props) {
super(props)
this.state = {
cog: null
}
autoBind(this)
}
selectCog(cog) {
this.setState({cog})
}
render(){
const { cog } = this.state
const cogSelector = (
<CogSelector selectCog={this.selectCog}/>
)
if(!cog) {
return cogSelector
}
return (
<>
<div>
{cogSelector}
</div>
<div>
{cog.name} Documentation
</div>
<div
dangerouslySetInnerHTML={{__html: cog.documentation}}>
</div>
</>
)
}
}
export default DocumentDisplayer
hence the cogSelector is rerendered, and it is no longer collapsed. I can then click it again, and it properly opens because selectCog doesn't cause a rerender.
I'm pretty sure this is just some horrible design flaw, but I would like my parent component to rerender without having to rerender the cogSelector. especially because they don't take any state from the parent. Can someone point me to a tutorial or documentation that explains this type of thing?
Assuming that Collapsible is a stateful component that is open by default I guess that the problem is that you use your component as a variable instead of converting it into an actual component ({cogSelector} instead of <CogSelector />).
The problem with this approach is that it inevitably leads to Collapsible 's inner state loss because React has absolutely no way to know that cogSelector from the previous render is the same as cogSelector of the current render (actually React is unaware of cogSelector variable existence, and if this variable is re-declared on each render, React sees its output as a bunch of brand new components on each render).
Solution: convert cogSelector to a proper separated component & use it as <CogSelector />.
I've recently published an article that goes into details of this topic.
UPD:
After you expanded code snippets I noticed that another problem is coming from the fact that you use cogSelector 2 times in your code which yields 2 independent CogSelector components. Each of these 2 is reset when parent state is updated.
I believe, the best thing you can do (and what you implicitly try to do) is to lift the state up and let the parent component have full control over all aspects of the state.
I solved this using contexts. Not sure if this is good practice but it certainly worked
render() {
return (
<DocContext.Provider value={this.state}>{
<>
<div>
<CogSelector />
</div>
{/*here is where we consume the doc which is set by other consumers using updateDoc */}
<DocContext.Consumer>{({ doc }) => (
<>
<div>
Documentation for {doc.name}
</div>
<pre>
{doc.documentation}
</pre>
</>
)}
</DocContext.Consumer>
</>
}
</DocContext.Provider>
)
}
then inside the CogSelector you have something like this
render() {
const { name, commands } = this.props
const cog = this.props
return (
//We want to update the context object by using the updateDoc function of the context any time the documentation changes
<DocContext.Consumer>
{({ updateDoc }) => (
<Collapsible
trigger={name}
onTriggerOpening={() => updateDoc(cog)}
onTriggerClosing={() => updateDoc(defaultDoc)}>
Commands:
<ul>
{commands.map((command, index) => {
return (
<li key={index}>
<Command {...command} />
</li>
)
}
)}
</ul>
</Collapsible>
)}
</DocContext.Consumer>
)
}
in this case it causes doc to be set to what cog was which is a thing that has a name and documentation, which gets displayed. All of this without ever causing the CogSelector to be rerendered.
As per the reconciliation algorithm described here https://reactjs.org/docs/reconciliation.html.
In your parent you have first rendered <CogSelector .../> but later when the state is changed it wants to render <div> <CogSelector .../></div>... which is a completely new tree so react will create a new CogSelector the second time

Access to outer react component from referenced component

I have a React.Component that renders references to others which are defined via const
Here is part of my code
const TodoList = ({data, remove}) => {
let source = data.map((todo) => {
return (<TodoItem todo={todo} id={todo.id} remove={remove}/>)
})
return (<ul>{source}</ul>)
}
export default class HomePage extends React.Component {
constructor(props) {
super(props);
console.log("Start home page")
this.state = {
data: []
}
this.getAllTasks = this.getAllTasks.bind(this);
}
render() {
return (
<div className={styles.content}>
<h1>Home Page</h1>
<p className={styles.welcomeText}>Thanks for joining!</p>
<button onClick={this.getAllTasks}>Request all tasks</button>
<button onClick={this.getSpecificTask}>Get specific task</button>
<button onClick={this.createTask}>Create task</button>
<button onClick={this.editTask}>Edit task</button>
<button onClick={this.deleteTask}>Delete task</button>
<div id="container">
<TodoForm addTodo={this.addTodo.bind(this)} />
<TodoList
todos={this.state.data}
remove={this.handleRemove.bind(this)} />
</div>
</div>
);
}
}
I am facing issues after adding TodoForm and TodoListto the render. First of them is inner state of TodoList is undefined. By being more specific:
page.js:85 Uncaught TypeError: Cannot read property 'map' of undefined
at TodoList (page.js:85)
First of all I haven't committed enough time to find out how that's all work under the hood (have a different main tech stack and tight deadline for this app). Source code shows data is obtain via autogenerated reference _ref3 that is passed from outer class.
I you see outer class defines and init the data structure, so data should be defined.
Do you see the issue I missed here?
Okay you are not destructuring properly
do it as this
const TodoList = ({todos, remove}) => {
let source = todos.map((todo) => {
return (<TodoItem todo={todo} id={todo.id} remove={remove}/>)
})
return (<ul>{source}</ul>)
}
You are passing prop as todos and destructuring it as data
Hope it helps
As other solution/answer mentioned you were not destructuring properly thus you encountered the error.
Apart from above solution you can alias it for local scope name data as below:
const TodoList = ({todos: data, remove}) => { // <---------------------
^^^^^^^^^^^
let source = data.map((todo) => {
return (<TodoItem todo={todo} id={todo.id} remove={remove}/>)
})
return (<ul>{source}</ul>)
}
You are getting an object in TodoList which happens to be your props which you are trying to destructure which has to match key by key but you can alias is using : for local name scoping generalized purpose.

Can't get a specific element from an array. React/Redux

I retrieve an array of data through fetch from an API. In my React Component, when I use mapStateToProps and .map(), I am able to display the contents of the array. However, if I try to get just one element from the array like array[0], it keeps returning undefined.
/* HomePage class Component: Ascendent of Banner */
class HomePage extends Component {
componentWillMount() {
this.props.fetchMovies();
}
render() {
const movie = this.props.movies[0];
return (
<div>
<Banner movies={this.props.movies} movie={movie} />
<Movies movies={this.props.movies} />
</div>
);
}
}
HomePage.propTypes = {
fetchMovies: PropTypes.func.isRequired,
movies: PropTypes.array.isRequired
};
const mapStateToProps = state => ({
movies: state.movies.movies
});
export default connect(
mapStateToProps,
{ fetchMovies }
)(HomePage);
/* Banner class Component: Descendent of HomePage */
class Banner extends Component {
render() {
const movieList = this.props.movies.map(movie => {
return <li>{movie.title}</li>;
});
return (
<div style={styles.BannerContainer}>
<div style={styles.Banner}>
<div style={styles.BannerText}>
<h1 style={styles.BannerTextHeader}>{this.props.movie.title}</h1>
<p style={styles.BannerTextParagraph}>
Arthur Curry learns that he is the heir to the underwater kingdom
of Atlantis, and must step forward to lead his people and be a
hero to the world.
</p>
<ul>{movieList}</ul>
<Button content={"Check It Out"} />
</div>
<div style={styles.BannerImage} />
<div style={styles.BannerOverlay} />
</div>
</div>
);
}
}
export default Banner;
I expect this.props.movie.title to equal this.props.movies[0].title, but the actual output is an error saying cannot get title of undefined.
The reason is that this.props.movies is undefined on first render )until you make the call to fetchMovies).
Consider checking if it exists first like this:
class HomePage extends Component {
componentWillMount() {
this.props.fetchMovies();
}
render() {
if (this.props.movies && this.props.movies[0]) {
const movie = this.props.movies[0];
return (
<div>
<Banner movies={this.props.movies} movie={movie} />
<Movies movies={this.props.movies} />
</div>
);
} else {
<div>Loading...</div>;
}
}
}
Why don't you access
{this.props.movie.title}
Like
{this.props.movie[0].title}
Seems more logical to me. And this might be the solution.
correct me if i am wrong.
And could you also console.log {this.props.movie}
Initially movies may be empty array and since you are accessing zero index position you should check it’s length before accessing zero index.
Change
const movie = this.props.movies[0];
To
if(this.props.movies.length){
const movie = this.props.movies[0];
console.log(movie);
}
Since movies is always an array so checking directly it’s length will resolve the issue

Fastest way to display data with ReactJS

I am trying to create a mud (multi user dungeon) client using Electron and ReactJS for myself for learning and challenging myself. But it seems I failed on this challenge.
I am using telnet-stream to get the data from server.
The data from the server has ansi codes since it's a telnet based communication.
The problem is the speed. I am not sure if I am doing it right but here is the component that is responsible for displaying data:
import React, { Component } from 'react';
import styles from './style.css';
type Props = {
output: Array<any>
};
export default class ActionWindow extends Component<Props> {
props: Props;
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate() {
this.scrollToBottom();
}
scrollToBottom() {
this.el.scrollIntoView({ behavior: 'smooth' });
}
render() {
const output = this.props.output.map(chunk => chunk
.replace('&', '&')
.replace('<', '<')
.replace('>', '&gt')
.replace(/\x1b\[(\d+);(\d+);?(\d+)?m/g, '</span><span class="c-$1 c-$2 x-$3">')
);
return (
<div className={styles.actionWindowWrapper}>
<span dangerouslySetInnerHTML={{ __html: output }} />
<div ref={el => { this.el = el; }} />
</div>
);
}
}
Is this the correct way, or there is a faster method? The data comes from the main App component by props.
Why are you using 'dangerouslySetInnerHTML'? Because you map the output to an html element, you could write:
render() {
return (
<div className={styles.actionWindowWrapper}>
this.props.output.map(chunk => chunk
.replace('&', '&')
.replace('<', '<')
.replace('>', '&gt')
.replace(/\x1b\[(\d+);(\d+);?(\d+)?m/g, '<span></span class="c-$1 c-$2 x-$3">')
<div ref={el => { this.el = el; }} />
</div>
);
}
I'm do not completely understand how you regular expresion '.replace(/\x1b[(\d+);(\d+);?(\d+)?m/g, '')' I don't think it outputs correct html because of the closing tag

React component rendering before data received from localStorage

I am having difficulties implementing a piece of React code that fetches and renders information from localStorage.
I have a gallery of thumbnail pictures (components) that upon a click, trigger a function that fetches information from a db for that particular picture and stores the data in localStorage. It also redirects the user to the ShowTale component where I display information about the picture.
class Tale extends Component {
handleClick(){
const pictureId = this.props._id;
this.props.fetchTale(pictureId);
}
render() {
const {_id, title, story, picture} = this.props;
return (
<div className="col-md-2 col-sm-6">
<div className="thumbnail">
<img src={picture} />
<div className="caption">
<h4>{title}</h4>
</div>
<p>
<Link onClick={this.handleClick.bind(this)} to="showTale" className="btn btn-primary">More Info</Link>
</p>
</div>
</div>
)
}
};
export default connect(null, actions)(Tale);
I set the data via the following action:
export function fetchTale(id){
return function(dispatch){
axios.get(`${ROOT_URL}/tale`, {params: {id: id}}).then(function(response) {
localStorage.setItem('fishTail', JSON.stringify(response.data));
})
}
};
The problem lies in that the ShowTale component, below, does not render the correct data. It renders the correct data on the first instance upon starting the application, but on subsequent requests, it renders the previous data. For example: I'll start the app, click on picture 1 renders 1, click on picture 2 renders 1, click on picture 3 renders 2, and so on. The data on localStorage is being correctly updates, but it appears the component is grabbing the data from localStorage before it is updated by the action.
class ShowTale extends Component {
constructor(props){
super(props)
this.state = {tale: JSON.parse(localStorage.getItem('fishTail'))}
}
componentWillMount(){
this.setState = JSON.parse(localStorage.getItem('fishTail'));
}
renderTale(){
const tale = this.state.tale;
console.log('the tale: ', tale);
const {title, story, picture, author} = tale;
return (
<div className="thumbnail">
<img className="image-responsive" src={picture}/>
<div className="caption-full">
<h4>{title}</h4>
<p>{story}</p>
<p><em>Submitted by: {author}</em></p>
</div>
</div>
)
}
render() {
return(
<div className="container showTale">
<div className="row">
<div className="col-sm-12">
{this.renderTale()}
</div>
</div>
</div>
)
}
};
export default ShowTale;
Any assistance with getting the pictures to show in sync with the data in localStorage will be greatly appreciated!
I'm using JSX so this may look weird to you.
You could put all the elements that need to change, in the state of your parent. Then the child component will be dumb, and just handle the content as it changes from the Tale parent component example:
class Tale extends Component {
// Parent Tale component handles fetching and setting state.
constructor(props){
super(props)
this.state = {
title:'',
story:'',
picture: Null,
author: ''
}
}
componentWillMount(){
this.fetch_info()
}
fetch_info(){
newObj = JSON.parse(localStorage.getItem('fishTail'))
setState({
title: newObj.title,
story: newObj.story,
picture: newObj.picture,
author: newObj.title
});
}
render() {
return(
<div className="container showTale">
<div className="row">
<div className="col-sm-12">
<ShowTale
title={this.state.title}
story={this.state.story}
picture={this.state.picture}
author={this.state.author} />
</div>
</div>
</div>
)
}
};
class ShowTale extends Component {
// Child ShowTale receives Props.
constructor(props){
super(props)
}
render(){
<div className="thumbnail">
<img className="image-responsive" src={this.props.picture}/>
<div className="caption-full">
<h4>{this.props.title}</h4>
<p>{this.props.story}</p>
<p><em>Submitted by: {this.props.author}</em></p>
</div>
</div>
}
export default ShowTale;
If this doesn't work, look at passing in a function to setState. Here is an example from the documentation.
Hope this example helps -- sorry it is in JSX!

Resources