React this.state.addroom.map is not a function - javascript

So I am trying to gen a div with a button onClick of a button but I get an error that is stopping me from doing this.
Error: TypeError: this.state.addroom.map is not a function
But I saw that when I click my button once it doesn't show the error but it doesn't generate the div with the button either.
Here is my code:
import React, { Component } from 'react';
import Select, { components } from 'react-select';
import styles from '../styles/loginsignup.css'
import axios from 'axios'
import nextId from "react-id-generator";
export default class AccomodationInfo extends Component {
constructor() {
super();
this.state = {
accomcate: null,
addroom: ['one'],
isLoading: true,
}
}
handleClick = event => {
const htmlId = nextId()
event.preventDefault()
const addroom = this.state.addroom
this.setState({ addroom: htmlId })
return (
<div>
{this.state.addroom.map(addrooms => (
<button key= {addroom.id} className={addrooms.modifier}>
{addrooms.context}
</button>
))}
</div>
);
}
render() {
return(
<div>
<button onClick={this.handleClick}>Add</button>
</div>
)
}
}
}
Anyone knows what causes it and how we can fix it?

There are a few things off with your code.
First of all the addroom in your state is a string array in your constructor, but in the handleClick method you set it like this.setState({ addroom: htmlId }) which will set it to a string and on a string type the map function is not defined, hence the error.
You should add an item to the array like this.setState({ addroom: [...this.state.addroom, htmlId] })
Secondly, in your handleClick you shouldn't return jsx, if you wan to render data for your addroom array, you should do it in the render method, and in the handleClick you should just modify the addroom state variable.
You can achieve this like:
render() {
return (
<div>
<button onClick={this.handleClick}>Add</button>
{this.state.addroom.map((addroom) => (
<button>{addroom}</button>
))}
</div>
)
}
Lastly, your addrom variable is a string array only, so you can't access id, modifier and context in an item in that array.

Related

How to call the function and pass the parameter from import in React js? [duplicate]

I have a seemingly trivial question about props and function components. Basically, I have a container component which renders a Modal component upon state change which is triggered by user click on a button. The modal is a stateless function component that houses some input fields which need to connect to functions living inside the container component.
My question: How can I use the functions living inside the parent component to change state while the user is interacting with form fields inside the stateless Modal component? Am I passing down props incorrectly?
Container
export default class LookupForm extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false
};
}
render() {
let close = () => this.setState({ showModal: false });
return (
... // other JSX syntax
<CreateProfile fields={this.props} show={this.state.showModal} onHide={close} />
);
}
firstNameChange(e) {
Actions.firstNameChange(e.target.value);
}
};
Function (Modal) Component
const CreateProfile = ({ fields }) => {
console.log(fields);
return (
... // other JSX syntax
<Modal.Body>
<Panel>
<div className="entry-form">
<FormGroup>
<ControlLabel>First Name</ControlLabel>
<FormControl type="text"
onChange={fields.firstNameChange} placeholder="Jane"
/>
</FormGroup>
);
};
Example: say I want to call this.firstNameChange from within the Modal component. I guess the "destructuring" syntax of passing props to a function component has got me a bit confused. i.e:
const SomeComponent = ({ someProps }) = > { // ... };
You would need to pass down each prop individually for each function that you needed to call
<CreateProfile
onFirstNameChange={this.firstNameChange}
onHide={close}
show={this.state.showModal}
/>
and then in the CreateProfile component you can either do
const CreateProfile = ({onFirstNameChange, onHide, show }) => {...}
with destructuring it will assign the matching property names/values to the passed in variables. The names just have to match with the properties
or just do
const CreateProfile = (props) => {...}
and in each place call props.onHide or whatever prop you are trying to access.
I'm using react function component
In parent component first pass the props like below shown
import React, { useState } from 'react';
import './App.css';
import Todo from './components/Todo'
function App() {
const [todos, setTodos] = useState([
{
id: 1,
title: 'This is first list'
},
{
id: 2,
title: 'This is second list'
},
{
id: 3,
title: 'This is third list'
},
]);
return (
<div className="App">
<h1></h1>
<Todo todos={todos}/> //This is how i'm passing props in parent component
</div>
);
}
export default App;
Then use the props in child component like below shown
function Todo(props) {
return (
<div>
{props.todos.map(todo => { // using props in child component and looping
return (
<h1>{todo.title}</h1>
)
})}
</div>
);
}
An addition to the above answer.
If React complains about any of your passed props being undefined, then you will need to destructure those props with default values (common if passing functions, arrays or object literals) e.g.
const CreateProfile = ({
// defined as a default function
onFirstNameChange = f => f,
onHide,
// set default as `false` since it's the passed value
show = false
}) => {...}
just do this on source component
<MyDocument selectedQuestionData = {this.state.selectedQuestionAnswer} />
then do this on destination component
const MyDocument = (props) => (
console.log(props.selectedQuestionData)
);
A variation of finalfreq's answer
You can pass some props individually and all parent props if you really want (not recommended, but sometimes convenient)
<CreateProfile
{...this.props}
show={this.state.showModal}
/>
and then in the CreateProfile component you can just do
const CreateProfile = (props) => {
and destruct props individually
const {onFirstNameChange, onHide, show }=props;

How to use forEach in react js

I want to create a function which iterate over all element with same class and remove a specific class.
It could be done easily using JavaScript.
const boxes = document.querySelectorAll(".box1");
function remove_all_active_list() {
boxes.forEach((element) => element.classList.remove('active'));
}
But how can I do this similar thing is ReactJs. The problem which I am facing is that I can't use document.querySelectorAll(".box1") in React but, I can use React.createRef() but it is not giving me all elements, it's only giving me the last element.
This is my React Code
App.js
import React, { Component } from 'react';
import List from './List';
export class App extends Component {
componentDidMount() {
window.addEventListener('keydown', this.keypressed);
}
keypressed = (e) => {
if (e.keyCode == '38' || e.keyCode == '40') this.remove_all_active_list();
};
remove_all_active_list = () => {
// boxes.forEach((element) => element.classList.remove('active'));
};
divElement = (el) => {
console.log(el);
el.forEach((element) => element.classList.add('active'))
};
render() {
return (
<div className="container0">
<List divElement={this.divElement} />
</div>
);
}
}
export default App;
List.js
import React, { Component } from 'react';
import data from './content/data';
export class List extends Component {
divRef = React.createRef();
componentDidMount() {
this.props.divElement(this.divRef)
}
render() {
let listItem = data.map(({ title, src }, i) => {
return (
<div className="box1" id={i} ref={this.divRef} key={src}>
<img src={src} title={title} align="center" alt={title} />
<span>{title}</span>
</div>
);
});
return <div className="container1">{listItem}</div>;
}
}
export default List;
Please tell me how can I over come this problem.
The short answer
You wouldn't.
Instead you would conditionally add and remove the class to the element, the component, or to the collection.map() inside your React component.
Example
Here's an example that illustrates both:
import styles from './Example.module.css';
const Example = () => {
const myCondition = true;
const myCollection = [1, 2, 3];
return (
<div>
<div className={myCondition ? 'someGlobalClassName' : undefined}>Single element</div>
{myCollection.map((member) => (
<div key={member} className={myCondition ? styles.variant1 : styles.variant2}>
{member}
</div>
))}
</div>
);
};
export default Example;
So in your case:
You could pass active prop to the <ListItem /> component and use props.active as the condition.
Alternatively you could send activeIndex to <List /> component and use index === activeIndex as the condition in your map.
Explanation
Instead of adding or removing classes to a HTMLElement react takes care of rendering and updating the whole element and all its properties (including class - which in react you would write as className).
Without going into shadow dom and why react may be preferable, I'll just try to explain the shift in mindset:
Components do not only describe html elements, but may also contain logic and behaviour. Every time any property changes, at the very least the render method is called again, and the element is replaced by the new element (i.e. before without any class but now with a class).
Now it is much easier to change classes around. All you need to do is change a property or modify the result of a condition (if statement).
So instead of selecting some elements in the dom and applying some logic them, you would not select any element at all; the logic is written right inside the react component, close to the part that does the actual rendering.
Further reading
https://reactjs.org/docs/state-and-lifecycle.html
Please don't hessitate to add a comment if something should be rephrased or added.
pass the ref to the parent div in List component.
...
componentDidMount() {
this.props.divElement(this.divRef.current)
}
...
<div ref={this.divRef} className="container1">{listItem}</div>
then in App
divElement = (el) => {
console.log(el);
el.childNodes.forEach((element) => element.classList.add('active'))
}
hope this will work. here is a simple example
https://codesandbox.io/s/staging-microservice-0574t?file=/src/App.js
App.js
import React, { Component } from "react";
import List from "./List";
import "./styles.css";
export class App extends Component {
state = { element: [] };
ref = React.createRef();
componentDidMount() {
const {
current: { divRef = [] }
} = this.ref;
divRef.forEach((ele) => ele?.classList?.add("active"));
console.log(divRef);
window.addEventListener("keydown", this.keypressed);
}
keypressed = (e) => {
if (e.keyCode == "38" || e.keyCode == "40") this.remove_all_active_list();
};
remove_all_active_list = () => {
const {
current: { divRef = [] }
} = this.ref;
divRef.forEach((ele) => ele?.classList?.remove("active"));
// boxes.forEach((element) => element.classList.remove('active'));
console.log(divRef);
};
render() {
return (
<div className="container0">
<List divElement={this.divElement} ref={this.ref} />
</div>
);
}
}
export default App;
List.js
import React, { Component } from "react";
import data from "./data";
export class List extends Component {
// divRef = React.createRef();
divRef = [];
render() {
let listItem = data.map(({ title, src }, i) => {
return (
<div
className="box1"
key={i}
id={i}
ref={(element) => (this.divRef[i] = element)}
>
<img src={src} title={title} align="center" alt={title} width={100} />
<span>{title}</span>
</div>
);
});
return <div className="container1">{listItem}</div>;
}
}
export default List;
Create ref for List component and access their child elements. When key pressed(up/down arrow) the elements which has classname as 'active' will get removed. reference

Function keeps on rendering when I try to add to a value to the sum. create-react-app

I am trying to create a function in which when a user clicks the add button className = "addButton" ; they will add the price value which is obtained from the .json that was fetched in the parent class this.props.products.
My problem is > When I try to render the ProductSquare component which contains the add button and a few other things related to the product, it will render in a infinite loop.
I am also selectivly targeting products that have the unique product._id
import React, { Component } from "react";
//css classes
import "./cssForComponents/ProductSquare.css";
import AddButton from "./addButton.js";
class ProductSquare extends Component {
constructor(props) {
super(props);
this.state = {
isLoaded: true,
sum: 0
};
this.onClickHandlerForAdd = this.onClickHandlerForAdd.bind(this);
this.getData = this.getData.bind(this);
}
onClickHandlerForAdd(price) {
this.setState({ sum: price });
}
getData() {
return this.props.products.map(
(product) =>
product._id === "5b77587c570ee12e768704da" ? (
<div className="ProductSquare" key={product.index}>
<button
className="AddButton"
onClick={this.onClickHandlerForAdd(product.price)}
>
{product.price}
</button>
<button className="InfoButton">{product.about}</button>
</div>
) : (
console.log("Results were not loaded")
)
);
}
render() {
console.log(this.state.sum);
return this.getData();
}
}
export default ProductSquare;
Your onClick handler is actually being called when you are doing the render; hence the state is changing, hence another render etc. It should be:
onClick={() => this.onClickHandlerForAdd(product.price)}
That way the onClick handler is registering a function to be called when clicked
You should avoid using arrow functions in the render method. To have a good understanding why, you can refer this article.
Why arrow functions are problematic inside render?
One way to to do this is to use a closure for the clickHandlers. You can modify your existing functions as below.
this.onClickHandlerForAdd = this.onClickHandlerForAdd.bind(this);
this.getData = this.getData.bind(this);
onClickHandlerForAdd = price => () => {
this.setState({ sum: price });
}
and
getData = () => {
return this.props.products.map(
(product) =>
product._id === "5b77587c570ee12e768704da" ? (
<div className="ProductSquare" key={product.index}>
<button
className="AddButton"
onClick={this.onClickHandlerForAdd(product.price)}
>
{product.price}
</button>
<button className="InfoButton">{product.about}</button>
</div>
) : (
console.log("Results were not loaded")
)
);
}

TypeError: this.state.userInfo.map is not a function

Sorry, I'm kinda new to react ,why I'm not being able to map through the data.
I have tried a different couple of things but nothing has helped.
Maybe the reason is that it's an object.
Can any one help?
import React, { Component } from "react";
import axios from "axios";
import "./Profile.css";
import ProfileCard from "../ProfileCard/ProfileCard";
class Profile extends Component {
state = {
userInfo: {}
};
componentDidMount() {
const { id } = this.props.match.params;
axios
.get(`/api/user/info/${id}`)
.then(
response => this.setState({ userInfo: { ...response.data, id } }),
() => console.log(this.state.userInfo)
);
}
render() {
let userInfoList= this.state.userInfo.map((elem,i)=>{
return(
<div> name={elem.name}
id={elem.id}</div>
)
})
console.log(this.state.userInfo);
return (
<div>
{/* <p>{this.state.userInfo}</p> */}
{/* <div >{userInfoList}</div>
<ProfileCard profilePic={this.state.userInfo} /> */}
</div>
);
}
}
export default Profile;
I think I understand what youre trying to do.
First you should change userInfo to an empty array instead of an empty object as others have stated.
Next since you are making an async api call you should use a ternary expression in your render method, because currently React will just render the empty object without waiting for the api call to complete. I would get rid of the userInfoList variable and refactor your code to the following:
RenderProfile = (props) => (
<div>
{props.elem.name}
</div>
)
{ this.state.userInfo
? this.state.userInfo.map(elem => < this.RenderProfile id={elem.id} elem={elem} /> )
: null
}
Let me know if it worked for you.

Render Random Item from State React.JS

Given some items stored in state, I want to be able to click a button and display a random item from that array. So far it only works on the first click and then it displays the same one letter after the first click.
What exactly is going on?
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
notes: ['hey', 'yo', 'sup'],
clicked: false
}
}
handleClick = () => {
this.setState({
clicked: true,
notes: this.state.notes[Math.floor(Math.random() *
this.state.notes.length)]
})
}
render() {
return (
<div className="App">
<button onClick={this.handleClick}>Random Note</button>
<h1>{this.state.clicked ? this.state.notes : ''}</h1>
</div>
)
}
}
Add selected note handling
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
notes: ['hey', 'yo', 'sup'],
selectedNote: null,
clicked: false
}
}
handleClick = () => {
this.setState({
clicked: true,
selectedNote: this.state.notes[Math.floor(Math.random() *
this.state.notes.length)]
})
}
render() {
return (
<div className="App">
<button onClick={this.handleClick}>Random Note</button>
<h1>{this.state.clicked && this.state.selectedNote}</h1>
</div>
)
}
}
You're overwriting the notes array in state in your handleClick method. Try using a different key (something like activeNote) in handleClick, then use that in your render method rather than this.state.notes.
So I think I solved it.
I ended up adding another state key randomWord and setting it equal to ''.
I then set the state of randomWord to that of this.state.notes when randomized.
Finally, I rendered this.state.randomWord
No clue if that's the correct approach but it's a working solution, lol.

Categories

Resources