Accessing props to mapped objects in render method [duplicate] - reactjs

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
I'm trying to create a dynamic list of views managed in a home component that can be saved and loaded. The save and load functions work fine but I get the following error when calling it in the span's returned from the map function in the Footer.js render method
Uncaught TypeError: Cannot read property 'props' of undefined
How can I access this function call in these elements?
Footer.js
export default class Footer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="footer hbox">
<div className="views hbox">
<span onClick={ (e) => this.props.getViewNames() }>Get Views</span>
<span onClick={ (e) => this.props.saveView('test3')}>Save View</span>
<span onClick={(e) => this.props.loadView('test3')}>Load View</span>
{this.props.getViewNames().map(function(viewName){
return (
<span onClick={(e)=>this.props.loadView(viewName)}>
{viewName}
</span>
);
})}
</div>
);
}
}

You forgot to use an arrow function for getViewNames.map(), and thus the lexical scope of this is not kept in your code -- which is interesting, considering you did use the correct approach everywhere else.
So just do:
this.props.getViewNames().map(viewName => {
...
});
Also see:
How to access the correct `this` context inside a callback?

The reason you cannot access props inside the map function is because this inside your function refers to the context of the map function, and not React Component. You will need to bind your map function to the React Component context by using bind(this) on the map function or arrow functions
Using bind(this)
export default class Footer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="footer hbox">
<div className="views hbox">
<span onClick={ (e) => this.props.getViewNames() }>Get Views</span>
<span onClick={ (e) => this.props.saveView('test3')}>Save View</span>
<span onClick={(e) => this.props.loadView('test3')}>Load View</span>
{this.props.getViewNames().map(function(viewName){
return (
<span onClick={(e)=>this.props.loadView(viewName)}>
{viewName}
</span>
);
}.bind(this))}
</div>
);
}
}
using arrow function
export default class Footer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="footer hbox">
<div className="views hbox">
<span onClick={ (e) => this.props.getViewNames() }>Get Views</span>
<span onClick={ (e) => this.props.saveView('test3')}>Save View</span>
<span onClick={(e) => this.props.loadView('test3')}>Load View</span>
{this.props.getViewNames().map((viewName) => {
return (
<span onClick={(e)=>this.props.loadView(viewName)}>
{viewName}
</span>
);
})}
</div>
);
}
}

Related

Get element was clicked in react using ref

I want to use react ref to get the element that was clicked, how i can do that and replace this javascript code in handleFavoritePairs function.
handleFavoritePairs = (e) => {
const pairs = e.target.closest('.coin-start').parentNode.getAttribute('symbol');
console.log(pairs);
}
render() {
coins[this.props.coin].map(coin => {
return (
<div className="landing-ticker-row" symbol={coin[0]} key={coin[0].toString()}>
<div className="coin-start" onClick={this.handleFavoritePairs}>
<i className="fas fa-star"></i>
</div>
<div className="coin-symbol">{coin[0]}</div>
</div>
);
})
}
There is no reason to use ref here and it gets tricky when mapping through arrays. Just make another component and pass the symbol down to that component. Then pass your onclick function down as well. Then you can pass the symbol back up to the parent when the user clicks on it like so:
handleFavoritePairs = symbol => {
console.log(symbol);
}
render() {
coins[this.props.coin].map(coin => {
return (
<NewComponent
key={coin[0].toString()}
symbol={coin[0]}
handleFavoritePairs={this.handleFavoritePairs}
/>
);
})
}
//new component
const NewComponent = props => (
<div className="landing-ticker-row">
<div className="coin-start" onClick={() => props.handleFavoritePairs(props.symbol)} >
<i className="fas fa-star"></i>
</div>
<div className="coin-symbol">{props.symbol}</div>
</div>
);
As you can see you are passing the symbol down to the new component along with your handleFavoritePairs function. Then you can pass the symbol into the onclick handler in your new component and pass it back up to your main component when the user clicks on it.

Pass a react value into a JS Alert

Im attempting to learn a little bit of react and struggling with this simple concept. I know its just a syntax thing that I'm unfamiliar with but I just can't get it down.
Im attempting to create a tic tac toe game, where each square has a value. When the square is clicked, it should pop up an alert box that shows "You've clicked box {Box Number}". I cannot figure out how to pass the value to the alert function.
class Square extends React.Component {
render() {
return (
<button className="square" onClick={function() {alert("Youve clicked box " + this.props.value);}}>
{this.props.value}
</button>
);
}
}
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} />;
}
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
class Game extends React.Component {
render() {
return (
<div className="game">
<div className="game-board">
<Board />
</div>
<div className="game-info">
<div>{/* status */}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
);
}
}
// ========================================
ReactDOM.render(
<Game />,
document.getElementById('root')
);
Looking at your code and based on your comment, the issue is that you're trying to access the props attribute of an object (this) that doesn't have the props attribute.
One way you can do this is to use an arrow function.
<button onClick={() => { alert(`Something ${this.props.value}`); }>Click Me</button>
Another way is to bind the function.
class Box extends React.Component {
constructor(props) {
super(props)
this.onClick = this.onClick.bind(this)
}
onClick() {
alert()
}
render() {
return (
<button onClick={this.onClick}>Click Me</button>
)
}
}
Alternatively, you can directly use an arrow function inside your class so you don't have to bind if you don't want to define one in the render function.
class Box extends React.Component {
onClick = () => {
alert()
}
render() {
return (
<button onClick={this.onClick}>Click Me</button>
)
}
}
Demo: https://jsfiddle.net/1wfyq68r/
You're re-binding the context of this by using a function declaration in your onClick. When you refer to this inside of that function, you're now referring to the context of that function, not the Square class (where this.props would reside).
Any easy fix is to use an arrow function instead–it does not rebind the context of this.
<button
className="square"
onClick={() => alert("Youve clicked box " + this.props.value)}
>
{this.props.value}
</button>
You need to set the context of the click handler function so it references the correct this. This can be done by either
Using an arrow function
() => alert("Youve clicked box " + this.props.value)
Explicitly binding
function() {
alert("Youve clicked box " + this.props.value);
}.bind(this)
A useful resource as to why this is required here

Reactjs - how to append a font-awesome icon to ref

I'm trying to use appendChild to add a fontawesome icon to a ref in react. I get the error:
Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
Using append instead of appendChild can only add text.
class Example extends React.Component {
handle = (e) => {
e.target.appendChild('<i className="fas fa-minus-circle"/>')
}
render() {
return (
<div className="container">
<button onClick={this.handle}>CLICK HERE</button>
</div>
)
}
}
Consider changing your approach a little. You can achieve the same result you're expecting with the code below, and it's a little more common to use state and conditional JSX in React:
class Example extends React.Component {
state = { images: [] }
handle = () => {
this.setState({ images: [...images, 'fas fa-minus-circle'] });
}
render() {
return (
<div className="container">
<button onClick={this.handle}>CLICK HERE</button>
{this.state.images.map((image, index) => {
<i key={index} className={image} />
})}
</div>
);
}
}
You can create ref and attach it to the element you want to add the icon.
class Example extends React.Component{
constructor(props){
super(props);
this.iconRef= React.createRef();
}
handle = (e) => {
e.prenventDefault();
this.iconRef.current.appendChild('<i className="fas fa-minus-circle"/>')
}
render() {
return (
<div className="container">
<button ref={this.iconRef} onClick={this.handle}>CLICK HERE</button>
</div>
)
}
}

How to remove an element in reactjs by an attribute?

I want to get unknown key attribute using known ID so that i may delete corresponding div.
I tried using document.getElementById("a").getAttribute('key'); , but it isn't working. May be my concept is wrong.
class PostAdded extends Component {
constructor(props) {
super();
this.deletepost = this.deletepost.bind(this);
}
deletepost() {
let ab =document.getElementById("a").getAttribute('key');
console.log(ab)
}
render() {
return (
<div>
{ this.props.posts.map((post, i) =>
<div id="a" key={`i-${post.title}`}>
<span> <h3>{post.title}</h3><p>{post.post}</p></span>
<input type="button" value="Delete" onClick={this.deletepost}/>
</div>
) }
</div>
)
}
}
export default PostAdded;
If you were able to delete the div, that probably wouldn't end up working for you anyway because any state change would cause a re-render and it would appear again. Instead, you could keep track of your posts in state and then remove one of the posts from state in your deletepost method.
class PostAdded extends Component {
constructor(props) {
super();
this.state = {
posts: props.posts
}
this.deletepost = this.deletepost.bind(this);
}
deletepost(index) {
const newPosts = this.state.posts.splice(index)
this.setState({posts: newPosts})
}
render() {
return (
<div>
{ this.state.posts.map((post, i) =>
<div id="a" key={`i-${post.title}`}>
<span> <h3>{post.title}</h3><p>{post.post}</p></span>
<input type="button" value="Delete" onClick={() => this.deletepost(i)}/>
</div>
) }
</div>
)
}
}
export default PostAdded;

Event Handler on component in Reactjs

This is not firing on the component but when I attach my event handler to a div it works. Do I need to pass a prop types function in my child component?
const buttonStyle = {
color: 'red'
};
class Button extends React.Component {
render() {
return (
<a className="social-button twitter">
<i href="#" className="fa fa-twitter"></i>
</a>
)};
}
class PanelButtons extends React.Component {
constructor(props){
super(props);
}
handleClick() {
console.log('this is:');
}
render() {
return (
<div>
<div onClick={(e) => this.handleClick(e)}> {/*this works attaching it to a div*/}
CLick me
</div>
<div className="social-buttons">
<Button onClick={(e) => this.handleClick(e)} />{/*does now work attaching it to a component*/}
</div>
</div>
)
}
}
ReactDOM.render(<PanelButtons />, document.querySelector('body'));
What you did basically, is passing a callback called onClick to the Button component. It will be accessible to you through the component's props.
class Button extends React.Component {
render() {
return (
<a className="social-button twitter" onClick={this.props.onClick}>
<i href="#" className="fa fa-twitter"></i>
</a>
)};
}
once the Button component's a element is clicked, the callback that you passed will be triggered (and handleClick will be called).
You should pass down the props into <Button /> component
class Button extends React.Component {
render() {
return (
<a className="social-button twitter" {...this.props}>
<i href="#" className="fa fa-twitter"></i>
</a>
)};
}
More reading: JSX spread attributes
An onClick on a <button /> would have worked as you expected.
But this is <Button /> a component which you created, the onClick will be sent as props which you can invoke through an onClick on the Button component's a tag like below, whose handleClick will callback the actual onClick on your PanelButtons component.
const buttonStyle = {
color: 'red'
};
class Button extends React.Component {
handleClick = (e) => {
this.props.onClick(e)
}
render() {
return (
<a className="social-button twitter" onClick={this.handleClick}>
<i href="#" className="fa fa-twitter"></i>
</a>
)};
}
class PanelButtons extends React.Component {
constructor(props){
super(props);
}
handleClick() {
console.log('this is:');
}
render() {
return (
<div>
<div onClick={(e) => this.handleClick(e)}> {/*this works attaching it to a div*/}
CLick me
</div>
<div className="social-buttons">
<Button onClick={(e) => this.handleClick(e)} />{/*does now work attaching it to a component*/}
</div>
</div>
)
}
}
ReactDOM.render(<PanelButtons />, document.querySelector('body'));
If you just want to add an onClick in the PanelButtons for each button, just modify your render a little bit like this by adding event listener on the div tag.
render() {
return (
<div>
<div onClick={(e) => this.handleClick(e)}> {/*this works attaching it to a div*/}
CLick me
</div>
<div className="social-buttons" onClick={(e) => this.handleClick(e)}>
<Button />{/*does now work attaching it to a component*/}
</div>
</div>
)
}

Resources