React/material ui raisedbutton executing onTouchTap on init - javascript

I am using react/redux/material ui and normally through out my website the components work fine. One 1 page there is something very very wierd going on.
I create a component like this:
class MyOwnComponent extends Component {
doSomething = (id) => {
alert('doSomething: id = ' + id )
}
render() {
return (
<RaisedButton secondary={true} label={'My label'} onTouchTap={this.doSomething(id)}/>
)
}
}
I have a raisedbutton from material ui and put it in the render method.
The thing is that when the page loads with the component in it the doSomething method is called. Even though it is only called in the onTouchTap in raisedbutton. Almost as if a bug in the raisedbutton is calling the onTouchTap method immediately instead when the button is clicked.
Does any body have a explanation for this really strange behaviour?
Thanks

You are giving to onTouchTap void, because that's what this.doSomething(id) returns .
this.doSomething(id)
is executed the firs time MyOwnComponent is rendered.
Instead you should do this :
class MyOwnComponent extends Component {
doSomething = () => {
const {id} = this.props.object;
alert('doSomething: id = ' + id )
}
render() {
return (
<RaisedButton secondary={true} label={'My label'} onTouchTap={this.doSomething}/>
)
}
}

The problem was that the return value of doSomething() function is returned and assigned to onTouchTap. Instead you should just pass the function name, without paranthesis.
One solution can be this.
class MyOwnComponent extends Component {
doSomething = () => {
alert('doSomething: id = ' + this.props.object.id );
}
render() {
return (
<RaisedButton secondary={true} label={'My label'} onTouchTap=
{this.doSomething}/>
);
}
}
Alternatively you can use also use
<RaisedButton secondary={true} label={'My label'} onTouchTap={()=>
this.doSomeThing(id) /> // eslint-disable-line

I tried your sollution. However when I use onTouchTap={() => this.doSomething(id)} the page wont load because the browser says JSX props should not use arrow functions.
And the id is from the props. At the top of the render method I say
const { id } = this.props.object

Related

How to pass function and data from component class to stateless class in React Native?

I am working with react native and I want to pass function and some data from Component class to another Stateless class, but I could not make to passing function and data part.
Here you can see my Component class:
class Classroom extends Component {
constructor(props, context) {
super(props, context);
};
state = {
isLightOn: false,
title : "Turn light on "
}
onPress() {
this.setState({isLightOn: !this.state.isLightOn})
console.log(this.state.isLightOn)
this.setState({title:this.state.isLightOn===false ?"Turn light off":"Turn light on"})
}
render() {
return (
<View style={styles.blue}>
<LightBulb isLightOn={this.state.isLightOn}> </LightBulb>
<LightButton onPress={this.onPress} isLightOn={this.state.isLightOn} title={this.state.title} > </LightButton>
</View>
);
}
}
Firstly, I want to pass isLightOn and title datas to my LightButton class (which mean to my stateless class). After that, I want to use onPress function inside of my Stateless class, but I cannot use. I am taking that error:
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
I also LightButton onPress={this.onPress} remove parenthesis, but still taking error.
Here is my my Stateless class
const LightButton = ({onPress,isLightOn,title}) => (
<View style={styles.red}>
<Button
title= {title}
onPress={() => {}
}
/>
</View>
)
I want to use onPress function and datas inside of the this class.
As a result, How can I pass function and data to that class?
The main issue here is that you need to declare onPress using an arrow function or bind it to the component's this value within the constructor. Otherwise it wouldn't have access to the correct this value. Other than that, the way you were passing props into components is perfectly fine.
I also merged your two set state calls in onPress to one as it's easier.
In LightButton, I set it up like this to pass the onPress function down to the button:
const LightButton = ({ onPress, isLightOn, title }) => (
<div style={{backgroundColor:'red'}}>
<Button title={title} onPress={onPress} />
</div>
);
(I set it up using react, but the issues at hand are more of a JS issue than a React/ReactNative one, so the advice should still be valid :) )
const { Component } = React;
const View = 'div';
const Button = (({title,onPress})=><button onClick={onPress}>{title}</button>);
const LightBulb = ({ isLightOn }) => {
return <div className={'LightBulb' + (isLightOn ? ' on' : '')} />;
};
const LightButton = ({ onPress, isLightOn, title }) => (
<div style={{backgroundColor:'red'}}>
<Button title={title} onPress={onPress} />
</div>
);
class Classroom extends Component {
state = {
isLightOn: false,
title: 'Turn light on ',
};
onPress=()=> {
console.log(this.state.isLightOn);
this.setState({
title:
this.state.isLightOn === false ? 'Turn light off' : 'Turn light on',
isLightOn: !this.state.isLightOn
});
}
render() {
return (
<div style={{backgroundColor:'blue'}}>
<LightBulb isLightOn={this.state.isLightOn}> </LightBulb>
<LightButton
onPress={this.onPress}
isLightOn={this.state.isLightOn}
title={this.state.title}
>Button</LightButton>
</div>
);
}
}
ReactDOM.render(<Classroom />, document.querySelector('#root'));
.LightBulb {
height: 50px;
width: 50px;
border-radius: 50px;
background-color: black;
}
.LightBulb.on {
background-color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
You can assign it like
const LightButton = ({onPress,isLightOn,title}) => (
...
onPress={onPress}
...
or with an arrow function if you need to pass arg inside
onPress={()=>onPress(someArg)}
do notice that you either don't put () at all, or twice () => func() for not run the function while it is just loads and not clicked.
unrelated directly to your issue but something that you encounter is inside onPress by doing like so
this.setState({isLightOn: !this.state.isLightOn})
console.log(this.state.isLightOn)
this.setState({title:this.state.isLightOn===false ?"Turn light off":"Turn light on"})
setState it is an async call, and therefore second setState usage not guaranteed to refer the state as you expect, use setState({ ... }, () => callback()) or all at one line and accords to prev state
this.setState({isLightOn: !this.state.isLightOn, title: !this.state.isLightOn===false ?"Turn light off":"Turn light on"})
First thing you did wrong is your state instantiating !
you need to instantiate your state in the constructor block like:
constructor(props, context) {
super(props, context);
this.state = { counter: 0 };
}
onPress() you use this for your function which is not recommended in react native or any other language , those are dedicated functions and methods of React Native
For passing a parameter or calling a function it is better to use these patterns ====>
onPress={() => urFunction()} with parenthesis or
onPress={urFunction} without parenthesis
Do the modifications I hope it helps <3

using a simple component (dumb) to create a list of buttons and use parent method

I'm trying to create a simple dashboard. I'm just exploring some new ideas I have in react and it's been so long I'm running into a strange problem I can't seem to understand.
I have a very simple class:
export default class Dashboard extends React.Component {
constructor(){
super();
}
HandleClick = (e) => {
if (e.name === "createEvent") {
console.log('event clicked');
}
console.log(e.name);
}
render() {
return(
<div className="row">
<ButtonList onClick={this.HandleClick}/>
</div>
)
}
}
and then I have a simple function outside of the class that creates a button list:
function ButtonList(props) {
return (
<button name="createEvent" onClick={props.HandleClick}>Create Event</button>
)
}
the idea behind this was instead of having so much stuff inside one superclass I wanted to separate simple functionality, like a button or command list if you will, that opon clicking would eventually change the state of the navbar.
I'm not sure how I would return that values of the button, or aside from that pass a parameter into the button from a child prop.
For example instead of doing HandleClick = (e) => and actually look for a parameter, how would I pass that in the child function where it gets used (if there were many more buttons)?
This is what you should be doing instead:
On your parent component, you can use arrow functions to pass the parameters within handleClick. This will allow you to listen to the events on your child ButtonList component with the parameters passed onto the method.
In addition, if you want to access to name attribute of your button, you should be calling event.target.name, as name is part of the target property of the Event interface.
export default class Dashboard extends React.Component {
constructor(){
super();
}
handleClick = (e) => {
if (e.target.name === "createEvent") {
console.log('event clicked');
}
console.log(e.target.name);
}
render() {
return(
<div className="row">
<ButtonList onClick={(e) => this.handleClick(e)} />
</div>
)
}
}
And on your ButtonList functional component, you should pass the onClick event to the onClick props which was defined as part of the ButtonList component.
function ButtonList(props) {
const onClick = (e) => {
props.onClick(e);
};
return (
<button name="createEvent" onClick={(e) => onClick(e)}>Create Event</button>
)
}
I have created a demo over here.

How can I pass a parent's state to be used within a child component?

I have a parent component where I am looping through an array and setting the state for clicked to that of the id of the clicked item in the array.
I am using this component elsewhere where I need to access this state.
How can I go about passing this state down?
Here is my loop:
{
filteredArray.map(link => {
return (
<div
key={link.id}
role="button"
style={{paddingBottom: 20}}
onClick={this.changeView(link.id)}
onKeyPress={() => {}}
tabIndex={0}
>
<Paragraph size="large">
<a className='heading__dropdown__link'>
{link.label}
</a>
</Paragraph>
</div>
)
})
}
Here is my function where I am setting the state for clicked
changeView(id) {
return (
() => this.setState({clicked: id})
)
}
And here is where I am using the above component:
How can I use the above state here?
<HeadingDropdown
expandedTitle="Change view"
links={links}
heading={currentLocation}
/>
Working off your last comment. If you passed down a function to be used as a prop in HeadingDropdown, you can use it to pass back up it's state-value to the Parent.
Function defined in Parent
class Parent extends React.Component{
state = {
headingDropdownvalues: {}
}
getHeadingDropdownState = (valueFromChild) => {
this.setState({
headingDropdownvalues: valueFromChild
})
}
render(){
<HeadingDropdown passupstate={this.getHeadingDropdownState}/>
}
}
So now your parent component is set-up to consume the state-value from HeadingDropdown.
Now we need to configure HeadingDropdown to actually pass up that value after clicking.
In HeadingComponent, we just need to update your changeView method to call the prop we passed down, after the state has been set. We do this by utilizing the 2nd argument of this.setState() which is a call-back.
changeView(id) {
return (
() => this.setState({
clicked: id
}, () => this.props.passupstate(this.state)) //right here you can pass in whatever you want
)
}
Also here is a sandbox for you to see how it works: https://codesandbox.io/s/jovial-thompson-ldg3n

React: Passing attribute from functional component to class component via onClick

Both of these first two snippets appear in a class component.
Here's the onClick handler:
selectChoice = (id) => {
console.log(id)
}
Here's where I call the functional component that generates both the id I need, and the onClick method.
<ReturnChoices choices={this.state.choices} selectChoice={() => this.selectChoice(id)}/>
Here's the functional component.
const ReturnChoices = ({choices, selectChoice}) => {
return choices.map(( choice , index) => (
<li key={index} id={index} onClick={() => { selectChoice(this.id) }}>
{choice}
</li>
))
}
For some reason, id is coming though as 'undefined'
pass the function itself, no need to wrap in additional function:
<ReturnChoices choices={this.state.choices} selectChoice={this.selectChoice}/>
Pass id given as argument from ReturnChoices to its caller function
<ReturnChoices choices={this.state.choices} selectChoice={(id) => this.selectChoice(id)}/>

Passing a custom argument to the eventListener in React [duplicate]

We should avoid method binding inside render because during re-rendering it will create the new methods instead of using the old one, that will affect the performance.
So for the scenarios like this:
<input onChange = { this._handleChange.bind(this) } ...../>
We can bind _handleChange method either in constructor:
this._handleChange = this._handleChange.bind(this);
Or we can use property initializer syntax:
_handleChange = () => {....}
Now lets consider the case where we want to pass some extra parameter, lets say in a simple todo app, onclick of item i need to delete the item from array, for that i need to pass either the item index or the todo name in each onClick method:
todos.map(el => <div key={el} onClick={this._deleteTodo.bind(this, el)}> {el} </div>)
For now just assume that todo names are unique.
As per DOC:
The problem with this syntax is that a different callback is created
each time the component renders.
Question:
How to avoid this way of binding inside render method or what are the alternatives of this?
Kindly provide any reference or example, thanks.
First: A simple solution will be to create a component for the content inside a map function and pass the values as props and when you call the function from the child component you can pass the value to the function passed down as props.
Parent
deleteTodo = (val) => {
console.log(val)
}
todos.map(el =>
<MyComponent val={el} onClick={this.deleteTodo}/>
)
MyComponent
class MyComponent extends React.Component {
deleteTodo = () => {
this.props.onClick(this.props.val);
}
render() {
return <div onClick={this.deleteTodo}> {this.props.val} </div>
}
}
Sample snippet
class Parent extends React.Component {
_deleteTodo = (val) => {
console.log(val)
}
render() {
var todos = ['a', 'b', 'c'];
return (
<div>{todos.map(el =>
<MyComponent key={el} val={el} onClick={this._deleteTodo}/>
)}</div>
)
}
}
class MyComponent extends React.Component {
_deleteTodo = () => {
console.log('here'); this.props.onClick(this.props.val);
}
render() {
return <div onClick={this._deleteTodo}> {this.props.val} </div>
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
EDIT:
Second: The other approach to it would be to use memoize and return a function
constructor() {
super();
this._deleteTodoListener = _.memoize(
this._deleteTodo, (element) => {
return element.hashCode();
}
)
}
_deleteTodo = (element) => {
//delete handling here
}
and using it like
todos.map(el => <div key={el} onClick={this._deleteTodoListener(el)}> {el} </div>)
P.S. However this is not a best solution and will still result in
multiple functions being created but is still an improvement over the
initial case.
Third: However a more appropriate solution to this will be to add an attribute to the topmost div and get the value from event like
_deleteTodo = (e) => {
console.log(e.currentTarget.getAttribute('data-value'));
}
todos.map(el => <div key={el} data-value={el} onClick={this._deleteTodo}> {el} </div>)
However, in this case the attributes are converted to string using toString method and hence and object will be converted to [Object Object] and and array like ["1" , "2", "3"] as "1, 2, 3"
How to avoid this way of binding inside render method or what are the
alternatives of this?
If you care about re-rendering then shouldComponentUpdate and PureComponent are your friends and they will help you optimize rendering.
You have to extract "Child" component from the "Parent" and pass always the same props and implement shouldComponentUpdate or use PureComponent. What we want is a case when we remove a child, other children shouldn't be re-rendered.
Example
import React, { Component, PureComponent } from 'react';
import { render } from 'react-dom';
class Product extends PureComponent {
render() {
const { id, name, onDelete } = this.props;
console.log(`<Product id=${id} /> render()`);
return (
<li>
{id} - {name}
<button onClick={() => onDelete(id)}>Delete</button>
</li>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
products: [
{ id: 1, name: 'Foo' },
{ id: 2, name: 'Bar' },
],
};
this.handleDelete = this.handleDelete.bind(this);
}
handleDelete(productId) {
this.setState(prevState => ({
products: prevState.products.filter(product => product.id !== productId),
}));
}
render() {
console.log(`<App /> render()`);
return (
<div>
<h1>Products</h1>
<ul>
{
this.state.products.map(product => (
<Product
key={product.id}
onDelete={this.handleDelete}
{...product}
/>
))
}
</ul>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Demo: https://codesandbox.io/s/99nZGlyZ
Expected behaviour
<App /> render()
<Product id=1... render()
<Product id=2... render()
When we remove <Product id=2 ... only <App /> is re-rendered.
render()
To see those messages in demo, open the dev tools console.
The same technique is used and described in article: React is Slow, React is Fast: Optimizing React Apps in Practice by François Zaninotto.
Documentation encourages to use data-attributes and access them from within evt.target.dataset:
_deleteTodo = (evt) => {
const elementToDelete = evt.target.dataset.el;
this.setState(prevState => ({
todos: prevState.todos.filter(el => el !== elementToDelete)
}))
}
// and from render:
todos.map(
el => <div key={el} data-el={el} onClick={this._deleteTodo}> {el} </div>
)
Also note that this makes sense only when you have performance issues:
Is it OK to use arrow functions in render methods?
Generally speaking, yes, it is OK, and it is often the easiest way to
pass parameters to callback functions.
If you do have performance issues, by all means, optimize!
This answer https://stackoverflow.com/a/45053753/2808062 is definitely exhaustive, but I'd say fighting excessive re-renders instead of just re-creating the tiny callback would bring you more performance improvements. That's normally achieved by implementing a proper shouldComponentUpdate in the child component.
Even if the props are exactly the same, the following code will still re-render children unless they prevent it in their own shouldComponentUpdate (they might inherit it from PureComponent):
handleChildClick = itemId => {}
render() {
return this.props.array.map(itemData => <Child onClick={this.handleChildClick} data={itemData})
}
Proof: https://jsfiddle.net/69z2wepo/92281/.
So, in order to avoid re-renders, the child component has to implement shouldComponentUpdate anyway. Now, the only reasonable implementation is completely ignoring onClick regardless of whether it has changed:
shouldComponentUpdate(nextProps) {
return this.props.array !== nextProps.array;
}

Categories

Resources