Assume that we have a reusable button component.
We give an onClick prop for the handle handleClick event.
<Button onClick={(e)=>{doSomething(e)}}/>
But also I want to change text property of button when user click.
To illustrate like this:
export default class Button extends React.Component{
static propTypes = {
onClick:PropTypes.func
}
constructor(props){
super(props)
this.state = {
text:'Not Clicked the Button'
}
this.handleClick = this.handleClick.bind(this)
}
handleClick = (e) => {
this.setState({text:'Clicked the Button'})
this.props.onClick(e)
};
render(){
const {text} = this.state
return (
<button onClick={this.handleClick}>{text}</button>
)
}
}
When trying like this it is getting an error show below:
TypeError: onClick is not a function
What should be for fix this error.
What is the best way handling events for the reusable components.
Thanks a lot.
I found the solution.
if there is not default value of prop func it gives an this error.
So at the begining it should consist of onClick default value as null.
static defaultProps = {
onClick:null
}
Related
I've done a research on this issue, but i can't seems to find the solution for my problem.
Other post of the same issue seems to be around binding issues, but i dont think that is the issue here.
When i load the page, the prop should be displayed, but it doesnt show anything.
When i write in the text form, i get the error: TypeError: this.props.AccountId is not a function",
and this is where i am stuck at.
import React from 'react';
class Accountparent extends React.Component {
constructor(props) {
super(props);
this.onFocusChange = this.onFocusChange.bind(this);
this.state = {accountobj: ''};
}
onFocusChange(value){
this.setState({accountobj: value});
}
render() {
return (
<div>
<Account value={this.state.accountobj} AccountId={this.onFocusChange}/>
</div>
)
}
}
class Account extends React.Component {
constructor(props) {
super(props);
this.EditAccount = this.EditAccount.bind(this)
this.state = {
accounts: [],
};
}
EditAccount(e){
this.props.AccountId(e.target.value)
}
render() {
const value = this.props.AccountId;
return (
<div>
<div>The props is: {this.props.dataid}</div>
<div>Write to textfield to pass data to parent: <input value={value} type="text" onChange={this.EditAccount}/></div>
</div>
)
}
}
export default Account;
EDIT:
As #Taki suggested, i should be exporting the parent.
That is the solution
Check your code,
EditAccount(e){
this.props.AccountId(e.target.value)
}
render() {
const value = this.props.AccountId;
. . .
In EditAccount method, you are using it like a function.
In render method you are deriving "value" from it.
It can either be a value or a function. NOT both.
As #Taki suggested, i should be exporting the parent. That is the solution
I try to using popovers from Reactstrap.
Here is the snippet of my code:
constructor(props) {
super(props);
this.state = {
popoverOpen: false
};
}
toggle = () => {
this.setState({popoverOpen: !this.state.popoverOpen})
};
<div>
<Button id="Popover1" type="button">
Launch Popover
</Button>
<Popover placement="bottom" isOpen={this.state.popoverOpen} target="Popover1" toggle={this.toggle}>
<PopoverHeader>Popover Title</PopoverHeader>
<PopoverBody>Hello there :)</PopoverBody>
</Popover>
</div>
The code above already works.
But, for now I want to separate the button on another component.
So, any example how to do that..?
Is it possible to taking the button ID from another component..?
Or, should I setState from another component..?
if so, please give an example or source to learn that.
I think this task can be solved with React state lifting.
The idea is to create Button component as you wish and pass to ittoggle function, so Button component will call it on each call. Also you may pass this.state.popoverOpen so Button component will know if popover currently open. Your min component will have the same state, but your Button component will have parent's state as props.
Here is example (this code is not tested! Use it as hint only!)
// ButtonComponent.js
// import React and other nesessary things
export default class ButtonComponent extends React.Component {
constructor(props) {
super(props);
}
render () {
return <Button id={this.porps.ButtonID} type="button" onClick={this.props.toggle}>
Launch Popover
</Button>
}
}
// mainComponent.js
import ButtonComponent from './ButtonComponent.js'
export default MainComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
popoverOpen: false
};
}
toggle = () => {
this.setState({popoverOpen: !this.state.popoverOpen})
};
render () {
return <div>
<ButtonComponent toggle={this.toggle.bind(this)} ButtonID={"Popover1"}/>
<Popover placement="bottom" isOpen={this.state.popoverOpen} target="Popover1" toggle={this.toggle}>
<PopoverHeader>Popover Title</PopoverHeader>
<PopoverBody>Hello there :)</PopoverBody>
</Popover>
</div>
}
}
Documentation describes how to add a ref to a class component when using ReactJS version 16.3+.
Here is a simplified and working example using two files:
MyForm.js file:
import React, { Component } from 'react';
import MyInput from "./MyInput";
class MyForm extends Component {
constructor(props) {
super(props);
this.myInput = React.createRef();
this.onClick = this.onClick.bind(this);
}
onClick(){
console.log(this.myInput.current.isValid());
}
render() {
return (
<div>
<MyInput ref={this.myInput} />
<button onClick={this.onClick}>Verify form</button>
</div>
);
}
}
export default MyForm;
MyInput.js file
import React, { Component } from 'react';
class MyInput extends Component {
isValid(){
return true;
}
render() {
return (
<div>
Name :
<input type="text" />
</div>
);
}
}
export default MyInput;
It works fine, console displays true when I click on MyForm button. But as soon as I add a function just before exporting my Component, errors are thrown. As example, I add a translation via react-i18n
MyInput.js file with export using a function
class MyInput extends Component {
isValid(){
return true;
}
render() {
const {t} = this.props;
return (
<div>
{t("Name")}
<input type="text" />
</div>
);
}
}
export default translate()(MyInput); // <=== This line is changing
Now, when I click on button, an error is thrown:
TypeError: this.myInput.current.isValid is not a function
The error disappear when I remove translate() in the last line.
I understood that the ref has been destroyed by the new component returned by translate function. It's an HOC. I read the Forwarding ref chapter, but I don't understand how to forward ref to the component returned by translate() function.
I have this problem as soon as I use translate from reacti18next and with the result of connect function from redux
I found a solution using onRef props and ComponentDidMount, but some contributors thinks this is an antipattern and I would like to avoid this.
Is there a way to create a wrapper that catch the HOC result of translate() or connect() and add ref to this HOC result ?
Some background:
I'm trying to consume a custom web component in a react app and trying to listen to events from the web component. I believe you can't just handle the events in the usual react way on custom web component.
i.e.
<custom-button onClick={this.clickHandler}></custom-button>.
I have tried this and it didn't work.
The problem:
So, I'm trying to listen to an event via addEventListener as you would do in vanilla js but the event handler is never getting called.
An example of this is below. I know <button/> isn't a custom web component but it demonstrates the problem:
import React, { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.buttonRef = React.createRef();
}
componentDidMount() {
// there are no errors attaching the handler
this.buttonRef.current.addEventListener("onClick", e => {
// this point is never reached
debugger;
console.log("onClick", e);
});
}
render() {
return <button ref={this.buttonRef}>click me</button>;
}
}
export default App;
Any help would be appreciated.
The event is called click, not onClick.
You don't need to use an eventListener to do it. In ReactJS, you can use a handler to the button, like this:
import React, { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
}
btnHandler = e => {
console.log("onClick",e)
}
render() {
return <button onClick={this.btnHandler}> click me </button>
}
}
export default App;
Take a look at the ReactJS Handling Events docs.
You are mixing two functionalities here,
button will only react to onClick events and your ref will only react to 'click' if the ref is clicked which it isn't.
So the button currently has no functionality.
From react doc:
function CustomTextInput(props) {
// textInput must be declared here so the ref can refer to it
let textInput = React.createRef();
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
This a solution to your problem. Hope is the right solution.
Also
<button onClick={this.onClick}>click me</button>
can be written as:
<button onClick={e => this.onClick({e, ...})}>click me</button>
to have access to custom props you want to pass along to your button Event Listener.
In this situation you Event Listener needs to look like:
onClick = (e, ...) => {
// this point is never reached
debugger;
console.log("onClick", e);
};
Please note the ... in there needs to be a key => value pair.
import React, { Component } from "react";
export default class App extends Component {
constructor(props) {
super(props);
}
onClick = e => {
// this point is never reached
debugger;
console.log("onClick", e);
};
render() {
return <button onClick={this.onClick}>click me</button>;
}
}
Also try not to use React References, unless you really need to. They are not intended to be used outside of React's core functionality.
who knows how change props in my component use refs in react js ?
<MyComponent
ref={'input1'}
name={'input1'}
label={interestsName}
disabled={ false}
onChange={this.myFunction}/>
after onChange i call function with
myFunction =()=>{console.log(this.rews[input1].props.disable);}
May I change props use refs without use state? Because I have many '15' components such as this component. Thanks.
You cannot change props from child class, for more please refer to link.
For your functionality use you can use state to change value on the change event. And one more thing you should keep the logic of changing component properties should remain inside a component. This will help us to maintain different states for different components.
class MyComponent extends React.Component {
constructor(props) {
this.state = {
disable: props.disabled
};
}
myFunction() {
console.log(this.state);
}
}
You can iterate over the above component and it can be used for 15 times and different states can be managed for every element
You shouldn't use ref in this case, you should use states to change your child props:
class MainComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
disable: false,
};
this.onChange = this.onChange.bind(this);
}
onChange() {
this.setState({ disable: true });
}
<MyComponent
name="input1"
label={interestsName}
disabled={this.state.disable}
onChange={this.onChange}
/>
}
In <MyComponent> use componentWillReceiveProps() to detect new props value.