I have the below where it should display images of beers retrieved from an API. Each image has a handleClick event which will direct them to a details page about this beer. My code below doesn't render the beers at all and goes straight to the details page of a random beer. Can anyone help me figure out why?
Thanks
export default class GetBeers extends React.Component {
constructor() {
super();
this.state = {
beers: [],
showMethod: false,
beerDetails: []
};
this.getBeerInfo = this.getBeerInfo.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleClick(details) {
this.setState({
showMethod: !this.state.showMethod,
beerDetails: details
});
}
render() {
if(this.state.showMethod) {
return (
<Beer details = {this.state.beerDetails}/>
);
}
else {
return (
<div>{this.state.beers.map(each=> {
return <img className = "img-beer" onClick = {this.handleClick(each)} src={each.image_url}/>
})}</div>
);
}
}
componentDidMount() {
this.getBeerInfo()
}
getBeerInfo() {
...gets info
}
}
When you use onClick like that you run the function at the render.
So you have to use arrow function:
Not Working:
<img className = "img-beer" onClick = {this.handleClick(each)} src={each.image_url}/>
Working:
<img className = "img-beer" onClick = {() => this.handleClick(each)} src={each.image_url}/>
The main issue is not calling the handle properly.
Also, I noticed that you are binding the functions in the constructor. It might be simpler to use ES6 function creation, so the scope of the class is bound to your handle method.
export default class GetBeers extends React.Component {
constructor() {
super();
this.state = {
beers: [],
showMethod: false,
beerDetails: []
};
}
handleClick = (details) => {
this.setState({
showMethod: !this.state.showMethod,
beerDetails: details
});
}
render() {
if(this.state.showMethod) {
return (
<Beer details = {this.state.beerDetails}/>
);
}
else {
return (
<div>{this.state.beers.map(each=> {
return <img className = "img-beer" onClick = {() => this.handleClick(each)} src={each.image_url}/>
})}</div>
);
}
}
componentDidMount() {
this.getBeerInfo()
}
getBeerInfo = () => {
...gets info
}
}
Related
Let's say I've a parent component A and a child B:
A:
class A {
constructor() {
this.state = {data: []};
}
handleClick = () => {
// api call
// set data state to the returned value from api
// call B's createTable method
}
render() {
return(
<div>
<button onClick={()=> this.handleClick()}>Fetch data</button>
<B data={this.state.data} />
</div>
}
}
B:
class B {
constructor() {
this.state = {...};
}
createTable = () => {
const { data } = this.props;
// do smth
}
render() {
return(...);
}
}
I want to call createTable method from A without using Refs.
What I've done so far is using componentDidUpdate life cycle method in B to check if data prop has changed or not, If it changed call createTable method but I want to know is this right? or there's a better way of doing it because I feel it is kinda hacky or maybe bad design.
class B {
constructor() {
this.state = {...};
}
componentDidUpdate(prevProps) {
const { data } = this.props;
if (data !== prevProps.data) {
this.createTable();
}
}
createTable = () => {
const { data } = this.props;
// do smth
}
render() {
return(...);
}
}
NOTE I don't want to use hooks either just class based component.
The following example might be useful
class Parent extends Component {
render() {
return (
<div>
<Child setClick={click => this.clickChild = click}/>
<button onClick={() => this.clickChild()}>Click</button>
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.getAlert = this.getAlert.bind(this);
}
componentDidMount() {
this.props.setClick(this.getAlert);
}
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
I'm new to React.js and maybe this question is kinda vague, please be kind. I am using react-image-gallery to create a gallery in my component. This gallery component has a prop of callback function onImageLoad which is called when an image is loaded (i am using lazy-loading). The problem is that this onImageLoad prop is able to do direct console.log('called') but it doesn't fire any event. So this works:
class Post extends React.Component {
render() {
return (
<ImageGallery onImageLoad={console.log('called')} />
)
}
}
But these don't work:
class Post extends React.Component {
EVENTNAME = e => {
console.log('called', e);
}
render() {
return (
<ImageGallery onImageLoad={this.EVENTNAME.bind(this)} />
)
}
}
This doesn't work either:
class Post extends React.Component {
EVENTNAME = e => {
console.log('called', e);
}
render() {
return (
<ImageGallery onImageLoad={e => this.EVENTNAME} />
)
}
}
Nor this one:
class Post extends React.Component {
EVENTNAME = e => {
console.log('called', e);
}
render() {
return (
<ImageGallery onImageLoad={e => console.log('called', e)} />
)
}
}
have tried this too:
class Post extends React.Component {
EVENTNAME = e => {
console.log('called', e);
}
render() {
return (
<ImageGallery onImageLoad={this.EVENTNAME} />
)
}
}
I'm guessing this is a problem of function scoping and something like that. Some hints and guidance will be appreciated. Thanks!
View Full File Code Here
are you getting error like ,'EVENTNAME' is not defined no-undef
if that's the case,
add declaration in front of function definition.
let EVENTNAME = e => {
console.log('called', e);
}
Problem solved define your images as
import React from "react";
import ImageGallery from "react-image-gallery";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
slidesLoaded: 0
};
}
images = [
{
original: "https://picsum.photos/id/1018/1000/600/",
thumbnail: "https://picsum.photos/id/1018/250/150/"
},
{
original: "https://picsum.photos/id/1015/1000/600/",
thumbnail: "https://picsum.photos/id/1015/250/150/"
},
{
original: "https://picsum.photos/id/1019/1000/600/",
thumbnail: "https://picsum.photos/id/1019/250/150/"
}
];
checkAndShowSlider = e => {
console.log("called", e);
let count = this.images.length;
let nowLoaded = this.state.slidesLoaded + 1;
if (nowLoaded === count) {
document.querySelector(".images.section").css("display", "block");
}
this.setState(() => {
return {
slidesLoaded: nowLoaded
};
});
};
render() {
return (
<div className="App">
<ImageGallery
onImageLoad={e => this.checkAndShowSlider(e)}
items={this.images}
showPlayButton={false}
thumbnailPosition="left"
useBrowserFullscreen={false}
/>
</div>
);
}
}
export default App;
I have a simple search bar which uses a react-autosuggest. When I create a suggestion, I want to attach an onClick handler. This onClick has been passed down from a parent class. When the suggestion is rendered however, this is undefined and therefore the click handler is not attached.
I have attached the component below, the logic which is not working is in the renderSuggestion method.
import Autosuggest from 'react-autosuggest'
import React from 'react'
export class SearchBar extends React.Component {
static getSuggestionValue(suggestion) {
return suggestion;
}
static escapeRegexCharacters(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: [],
listOfValues: this.props.tickers
};
}
onChange = (event, { newValue, method }) => {
this.setState({
value: newValue
});
};
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: this.getSuggestions(value)
});
};
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
renderSuggestion(suggestion) {
return (
<span onClick={() => this.props.clickHandler(suggestion)}>{suggestion}</span>
);
}
getSuggestions(value) {
const escapedValue = SearchBar.escapeRegexCharacters(value.trim());
if (escapedValue === '') {
return [];
}
const regex = new RegExp('^' + escapedValue, 'i');
return this.state.listOfValues.filter(ticker => regex.test(ticker));
}
render() {
const { value, suggestions } = this.state;
const inputProps = {
placeholder: "Search for stocks...",
value,
onChange: this.onChange
};
return (
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={SearchBar.getSuggestionValue}
renderSuggestion={this.renderSuggestion}
inputProps={inputProps} />
);
}
}
This is becuase you need to bind "this" to your function.
If you add this code to your constructor
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: [],
listOfValues: this.props.tickers
};
//this line of code binds this to your function so you can use it
this.renderSuggestion = this.renderSuggestion.bind(this);
}
It should work. More info can be found at https://reactjs.org/docs/handling-events.html
In the scope of renderSuggestion, this isn't referring to the instance of the class.
Turning renderSuggestion into an arrow function like you've done elsewhere will ensure that this refers to the instance of the class.
renderSuggestion = (suggestion) => {
return (
<span onClick={() => this.props.clickHandler(suggestion)}>{suggestion}</span>
);
}
I'm beginner on react and i've written the code below:
class Note extends React.Component {
constructor(props) {
super(props);
this.state = {editing: false};
this.edit = this.edit.bind(this);
this.save = this.save.bind(this);
}
edit() {
// alert('edit');
this.setState({editing: !this.state.editing});
}
save() {
this.props.onChange(this.refs.newVal.value, this.props.id);
this.setState({editing: !this.state.editing});
// console.log('save is over');
}
renderForm() {
return (
<div className="note">
<textarea ref="newVal"></textarea>
<button onClick={this.save}>SAVE</button>
</div>
);
}
renderDisplay() {
return (
<div className="note">
<p>{this.props.children}</p>
<span>
<button onClick={this.edit}>EDIT</button>
<button onClick={this.remove}>X</button>
</span>
</div>
);
}
render() {
console.log(this.state.editing);
return (this.state.editing) ? this.renderForm()
: this.renderDisplay()
}
}
class Board extends React.Component {
constructor(props){
super(props);
this.state = {
notes: []
};
this.update = this.update.bind(this);
this.eachNote = this.eachNote.bind(this);
this.add = this.add.bind(this);
}
nextId() {
this.uniqeId = this.uniqeId || 0;
return this.uniqeId++;
}
add(text) {
let notes = [
...this.state.notes,
{
id: this.nextId(),
note: text
}
];
this.setState({notes});
}
update(newText, id) {
let notes = this.state.notes.map(
note => (note.id !== id) ?
note :
{
id: id,
note: newText
}
);
this.setState({notes})
}
eachNote(note) {
return (<Note key={note.id}
id={note.id}
onChange={this.update}>
{note.note}
</Note>)
}
render() {
return (<div className='board'>
{this.state.notes.map(this.eachNote)}
<button onClick={() => this.add()}>+</button>
</div>)
}
}
ReactDOM.render(<Board />,
document.getElementById('root'));
In render(), onClick event has a function, that is, if used in this way: {this.add} the following error is created:
Uncaught Error: Objects are not valid as a React child (found: object with keys {dispatchConfig, _targetInst, nativeEvent, type, target, currentTarget, eventPhase, bubbles, cancelable, timeStamp, defaultPrevented, isTrusted, view, detail, ...})
Why? while in the eachNote() method this command is used:
onChange={this.update}
And there was no error.
Someone can tell me the reason? thanks.
The problem is that in the add function you are taking an argument text and setting it in the state so when you call onClick={() => this.add()}, you are not passing any argument to add function and hence in its definition text is undefned and hence state note is set as undefined.
However if you directly call it like onClick={this.add} , the add function receives the event object as a parameter and hence it sets state note to be an event object which you are using to render
onClick={this.add} will pass the click event to this.add.
So what you need to do is either:
onClick={e => this.add('some text')} or similar.
If you want to onClick={this.add} you have to ensure that your add method is: add(event) { ... } instead.
The <Note /> component does not contain a render() method to return anything. Add a render() method and return something.
class Note extends React.Component {
constructor(props) {
super(props);
this.state = {editing: false};
this.edit = this.edit.bind(this);
}
edit() {
// alert('edit');
this.setState({editing: !this.state.editing});
}
render() {
return (
<div>Render something</div>
)
}
}
class Board extends React.Component {
constructor(props){
super(props);
this.state = {
notes: []
};
this.update = this.update.bind(this);
this.eachNote = this.eachNote.bind(this);
this.add = this.add.bind(this);
}
nextId() {
this.uniqeId = this.uniqeId || 0;
return this.uniqeId++;
}
add(text) {
let notes = [
...this.state.notes,
{
id: this.nextId(),
note: text
}
];
this.setState({notes});
}
update(newText, id) {
let notes = this.state.notes.map(
note => (note.id !== id) ?
note :
{
id: id,
note: newText
}
);
this.setState({notes})
}
eachNote(note) {
return (<Note key={note.id}
id={note.id}
onChange={this.update}>
{note.note}
</Note>)
}
render() {
return (<div className='board'>
{this.state.notes.map(this.eachNote)}
<button onClick={() => this.add()}>+</button>
</div>)
}
}
ReactDOM.render(<Board />,
document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="root"></div>
I have a button in render(), and I want it's onClick() to set the state. I know you shouldn't be setting the state in render() because it causes an infinite loop, so how should I go about this?
function initialState(props) {
return {
edit: false,
value: props.value,
};
}
export default class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = initialState(props);
}
onCancel() {
this.setState({ edit: false, value: this.props.value });
}
onClick() {
this.state.edit ? this.onCancel() : this.setState({ edit: true });
}
render() {
return (
<div onClick={this.onClick}>
BUTTON
</div>
);
}
Updated to show what the code I'm trying now and the warning I'm getting thousands of times:
Warning: setState(...): Cannot update during an existing state transition (such as
within `render` or another component's constructor). Render methods should be a
pure function of props and state; constructor side-effects are an anti-pattern, but
can be moved to `componentWillMount`.
warning # warning.js?0260:44
getInternalInstanceReadyForUpdate # ReactUpdateQueue.js?fd2c:51
enqueueSetState # ReactUpdateQueue.js?fd2c:192
ReactComponent.setState # ReactComponent.js?702a:67
onCancel # mybutton.js?9f63:94
onClick # mybutton.js?9f63:98
render # mybutton.js?
...
Not really sure what you want to do since the previous answers didn't solve the issue. So if you provide some more information it might get easier.
But here is my take on it:
getInitialState() {
return (
edit: true
);
}
handleEdit() {
this.setState({edit: true});
}
handelCancel() {
this.setState({edit: false});
}
render() {
var button = <button onClick={this.handelEdit}>Edit</button>
if(this.state.edit === true) {
button = <button onClick={this.handelCancel}>Cancel</button>
}
return (
<div>
{button}
</div>
);
}
To set the state for your use case you need to set the state somewhere but I wouldn't do it this way. I would bind a function to the onClick event.
function initialState(props) {
return {
edit: false,
value: props.value,
};
}
export default class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = initialState(props);
this.handleButtonClick = this.onClick.bind(this);
}
onCancel() {
this.setState({ edit: false, value: this.props.value });
}
onClick() {
this.state.edit ? this.onCancel() : this.setState({ edit: true });
}
render() {
return (
<div onClick={this.handleButtonClick}>
BUTTON
</div>
);
}
Look here for more information
Try to make use of arrow functions to bind onBtnClick and onCancel function to the context and see if it solves your problem.
function initialState(props) {
return {
edit: false,
value: props.value,
};
}
export default class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = initialState(props);
}
onCancel = ()=> {
this.setState({ edit: false, value: this.props.value });
}
onBtnClick = () => {
this.state.edit ? this.onCancel() : this.setState({ edit: true });
}
render() {
return (
<div onClick={this.onBtnClick}>
BUTTON
</div>
);
}