Cannot read property of undefined when using map to render components - javascript

I'm new to React and still pretty new to programming in general and I've been practicing a bit just to get the basics down.
I currently have this:
class App extends Component {
constructor(props){
super(props);
this.state={strings: [],
textAreas: [<TextareaComponent ref='index0' onInput={this.onInputHandler} />,
<TextareaComponent ref='index1' onInput={this.onInputHandler} />]};
this.onInputHandler = this.onInputHandler.bind(this);
}
onInputHandler(){
const stringsArray = this.refs['index1'].refs.content.value.split(/\r|\n/);
this.setState({strings: (stringsArray)});
}
render(){
console.log(this.state.textAreas);
const renderTextareas = this.state.textAreas.map((textArea, index) => {
return <div key={index}>{textArea}</div>
})
return(
<div>
{renderTextareas}
</div>
);
}
};
class TextareaComponent extends Component {
constructor(props){
super(props);
}
render(){
return(
<div>
<textarea ref='content' onChange={this.props.onInput}></textarea>
</div>
);
}
};
Right now, my plan is to add a button that would push a new TextareaComponent to the textAreas array to render an additional textarea to the DOM. Currently I've just set the 2nd textarea as the element that I'd like to get values from.
Everything appears on the screen, but my problem is that I get this error when I type on any of the textareas that were rendered: Uncaught TypeError: Cannot read property 'index1' of undefined.
I noticed that when I just directly place the Textarea component with a ref of 'index1' on the parent component, the console logs the values properly and it seems to be able to read the 'index1' property. I'm not really sure where I'm getting it wrong.
Thanks in advance

You need to bind the onInputHandler function while passing it as a prop to the
TextareaComponent component
class App extends Component {
constructor(props){
super(props);
this.state={strings: [],
textAreas: [<TextareaComponent ref='index0' onInput={this.onInputHandler.bind(this)} />,
<TextareaComponent ref='index1' onInput={this.onInputHandler.bind(this)} />]};
this.onInputHandler = this.onInputHandler.bind(this);
}
onInputHandler(){
const stringsArray = this.refs['index1'].refs.content.value.split(/\r|\n/);
this.setState({strings: (stringsArray)});
}
render(){
console.log(this.state.textAreas);
const renderTextareas = this.state.textAreas.map((textArea, index) => {
return <div key={index}>{textArea}</div>
})
return(
<div>
{renderTextareas}
</div>
);
}
};
class TextareaComponent extends Component {
constructor(props){
super(props);
}
render(){
return(
<div>
<textarea ref='content' onChange={this.props.onInput}></textarea>
</div>
);
}
};

Related

Append array from constructor in react

I have this class component where I'd like to fetch the response from server to an state array so I can pass further the elements to another component as props, so far I have this:
export default class MainApp extends Component {
state = {
posts: [],
}
constructor(props){
super(props);
const request = new FetchRequest();
request.setAmount(this.props.amount);
request.setUserid(this.props.token);
request.setSeenpostsList(this.props.seenPosts);
var stream = client.fetchPosts(request, {});
stream.on('data', function(response) {
this.setState({
posts: [...this.state.posts, response.array]
})
});
}
render(){
return(
<div className="main-app">
<Navbar />
<Postbox token = {this.props.token}/>
{this.state.posts.map(element =>
<Postcard username = {element[0]}/>
)}
</div>
)
}
With this code I get TypeError: Cannot read properties of undefined (reading 'posts').
What is the correct way to do it?
Your state needs to be initialised within the constructor() {}
constructor(props) {
super(props);
this.state = {
posts: []
}
}
Here's info you might also want to read

How to create a List/Detail View in React

I need to implement a kind of Master/Detail View for a Web Application in React. Since the app should be integrated into a CakePHP app I can't use React Router for handling the routes (since CakePHP would process them).
I have a List of Items and want to navigate through them, showing a Detail View. Items are nested, so there're SubItems to navigate to.
For now I got a ItemList Component, showing a list of Cards with a clickhandler. How can I change the View without changing the url?
ItemList Component looks like:
class ItemList extends React.Component {
constructor(props) {
super(props);
this.state = {
itemList: []
}
}
componentDidMount() {
fetchItems(...)
}
render() {
return(
<div>
{this.state.itemList.map(item => (
<Item key={item.id} item={item} />
))}
</div>
);
}
}
Item Component looks like:
class Item extends React.Component {
constructor(props) {
super(props);
this.state = {
item: props.item,
}
}
handleClick = () => {
// How to navigate to another Component?
}
render() {
return(
<div>
<div className="card my-2" onClick={this.handleClick}>
<div className="card-body">
<h5 className="card-title">{this.state.item.title}</h5>
<p className="card-text">{this.state.item.description}</p>
</div>
</div>
</div>
);
}
}
Thanks in advance!
You should have a parent component (let's say MainView) that has a state (let's say selectedItemId).
class MainView extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedItemId: [null]
}
}
componentDidMount() {
}
render() {
return(
{!selectedItemId && (<ItemList />)}
{selectedItemId && (
<ItemDetail id={selectedItemId} />
)}
);
}
}
As you can see, it renders different components based on the selectedItemId state value.
Inside the ItemList handleClick you call the setState of the parent MainView to set the selected item ID.
So using conditional rendering inside the render() function of MainView you can render the ItemList when no item is selected and ItemDetail when you have selected one.
I'm not really used to ES6 syntax components so my code can be wrong somewhere, but you can get the message ;)

React - Passing props from Array to Div

I'm developing a more complex example of passing props from a component to another. In this case, it's the content of an array(when I click on that content) to a <div>.
You can find the code here: https://codesandbox.io/s/509j5npkwx
(Please check the code in the link above)
TextBox <div> component:
export class TextBox extends React.Component {
constructor(props) {
super(props);
this.state = {
content: "Select A Node To See Its Data Structure Here..."
};
this.changeContent = this.changeContent.bind(this);
}
changeContent(newContent) {
this.setState({
content: newContent
});
}
render() {
return (
<div className="padd_top">
<div className="content_box">{this.state.content}</div>
</div>
);
}
}
export default TextBox;
FileTree component:
export class FileTree extends React.Component {
constructor(props) {
super(props)
this.state = {
activeNode: null
}
this.setActiveNode = this.setActiveNode.bind(this)
}
setActiveNode(name) {
this.setState({ activeNode: name })
}
render() {
return (
<div className="padd_top">{
renderTree(
this.props.root || root,
this.setActiveNode,
this.state.activeNode
)
}
<TextBox />
</div>
)
}
}
I'm trying to do something like this, for further understanding: http://alexcurtis.github.io/react-treebeard/
I understood how to prepare the <div> to receive new information, by substituting the "Select A Node To See Its Data Structure Here..." when I click one of the elements belonging to the file tree.
I also understood how to prepare the file tree to pass content <div>, but in this case, I'm confused about where and how should I apply to the right component.
I'm new to React JS. If you have any tips for me about this issue, I'm very grateful.
Thank you.
I changed a bit the structure of my project, and now I'm looking forward to put <TextBox> and <FileTree> side by side.
More specifically, like this:
export class App extends React.Component {
render() {
return (
<div>
<div className="col-md-12">
<SearchEngine />
</div>
<div className="col-md-6">
<FileTree />
</div>
<div className="col-md-6">
<TextBox content={this.props.activeNode} />
</div>
</div>
);
}
}
I tought it wouldn't be different to pass props to <App>, but again I might be missing something, because it's not passing properly. What is missing here?
Thanks again.
I'm not sure if I understood your question.
Here is my fork: https://codesandbox.io/s/50pv75q8ll
Basically, you pass the activeNode to < TextBox />. Look at line 126 of index.js.
And then, in text_box.js use componentWillReceiveProps() to set the TextBox state with the content prop. Line 18 of text_box.js.

React : How to pass values from grandchild through child to parent?

I have a parent,child and grandchild component. I have different input fields and want to pass the values from grandchild to child to parent where eventually i set the state with the values. I havent included all of my code, but doing it like that is necessary because of other things in my code, which I didnt include in this post as its irrelevant. Im not sure how to do that and tried to implement what I found online, however, its not working. Any ideas? Thanks!!
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
input: {}
};
this.changeName = this.changeName.bind(this);
this.handleInput = this.handleInput.bind(this);
}
changeName(newName) {
this.setState({
name: newName
});
}
handleInput() {
console.log("helloooooo", this.state.name)
}
render() {
return (
<div>
<Child onChange={this.changeName} onClick={this.handleInput}/>
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleInput2 = this.handleInput2.bind(this);
}
handleChange(e) {
this.props.handleChange(e);
}
handleInput2() {
this.props.onClick()
}
render() {
return (
<div>
<GrandChild onChange={this.handleChange}/>
<input type="submit" onClick={this.handleInput2}/>
</div>
)
}
}
class GrandChild extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleInput2 = this.handleInput2.bind(this);
}
handleChange(e) {
const input = this.props.input;
input[name] = e.target.value;
}
render() {
return (
<div>
<input name="firstname" onChange={this.handleChange}/>
<input name="lastname" onChange={this.handleChange}/>
</div>
)
}
In real life everything is easier. For every component you answer following questions.
What data the component will receive?
Does it emit any event?
That's the props of the component.
So no matter how the relationship between your components is... Just answer those questions and you will be good.
Example:
I have a TodoList that contains a list of TodoItem elements. (Parent)
I have a TodoItem that displays the content of the TodoItem. (Child)
I have a Checkbox that displays a check box. (GrandChild)
a CheckBox receives a boolean saying isSelected and emit and event onChange. That's all what I know.
a TodoItem receives a Todo and emit onChange. That's all I care.
When you put everything together, TodoList has a todos, and pass todos[i] to its child and todos[i].isSelected to its grandchild, but that is what you don't need to care about. All what you care is:
What data the component will receive? Does it emit any event?
At the component level.

React Uncaught TypeError: search.map is not a function

I am trying to play around with the wikipedia API. I'm using Axios to make a request for the data. When I go to map through the prop passed through from App's state, I get the following error:
Uncaught TypeError: search.map is not a function
I have checked that the intended value is an array. It seems to be, I can manipulate it as such in the react dev tools console. It also has a proto of Array, so I'm confused as to why I can't do this.
Root Component:
class App extends React.Component
{
constructor()
{
super();
this.state = {search: {}}
this.wikiSearch();
}
wikiSearch()
{
axios
.get('https://en.wikipedia.org/w/api.php?action=opensearch&search="test"')
.then ((result) => {
result.data.shift();
this.setState({search: result.data});
});
}
render ()
{
return(
<div id="container">
<Header />
<SearchBar />
<SearchList search={this.state.search} />
</div>
);
}
}
export default App;
The component that uses state data from App
class SearchList extends React.Component
{
render()
{
let search = this.props.search;
search.map((element) => {
});
return(
<div id='SearchList'>
</div>
);
}
}
You need to initialize search as an empty array variable, not object, in your component state so that it's not throw erro on calling map method on it like this:
this.state = {search: []}
not like this:
this.state = {search: {}}

Categories