How come that onClick doesnt work in React in some situations? - reactjs

for some weird reason the onClick doesnt work in my React App.
<GorillaSurfIn
onClick={() => {
console.log('gorilla clicked');
setShouldGorillaSurfOut(true);
}}
/>
As you can see I console.logged it, but I dont see anything in the console.
Very strange. Here is the SandCode box.
What it should do is render the GorillaSurfOut once we click on GorillaSurfIn.

Your GorillaSurfIn component never uses the props its passed. You will need to have it do something with the onClick prop, probably passing it into the div.
const GorillaSurfIn = (props) => {
return (
<div id="moving-gorilla" onClick={props.onClick}>
<motion.img
animate={{
x: 530,
y: 350,
transition: { duration: 3 }
}}
id="gorilla-surf-gif"
src={require('../images/surfing_gorilla.gif')}
/>
</div>
);
};

I think it's because GorillaSurfIn is a custom component, right ?
The onClick event only apply to DOM Elements, such as div.
To make it work, you would have to get the props inside the component, and apply it to a div inside the component

What Chandelier Axel said was write.
onClick event listener is for React elements and not for React Components. To add an event listener for a React Component you have to do something like below.
function Test({ customClickEventHandler }) {
return <div onClick={customClickEventHandler}><h1>Test Button</h1></div>;
}
class Main extends React.Component {
render() {
return (
<Test customClickEventHandler={() => console.log('Clicked')} />
);
}
}
<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>
So, you can pass a function that has to be called when clicked as props to the child component and then add an onClick listener to the top element that contains all other elements in your child component. It works as you expected. In this situation you have to pay attention to the this keyword make sure you bind the function or use an arrow function

Related

How to bypass two clicks needed to blur contenteditable div and click on outside button?

I have a contenteditable div that holds its own state which allows me to perform checks on its own length and display warnings.
This editable div is called from a form-like component, which also holds its state, and retrieves the innerText of the contenteditable div when onBlur is fired.
However, if a user clicks on any button when the div was focused, it will only handle the onBlur() event, but not thereafter handle the onClick that was supposed to have occurred when the button was clicked. Therefore from a div focused state to a click (for example submit) two clicks are needed, when I would only like for there to be one.
The behavior I would like and expect would be this:
class Form extends React.Component {
state = {
name: this.props.initialName
}
handleBlur(e) {
console.log('handleBlur was called');
}
handleSubmit(e) {
console.log('handleSubmit was called');
}
render() {
const { name } = this.state;
return (
<div style={{width:'400px'}}>
<div>This is a contenteditable div: </div>
<div
style={{
width: '70px',
height: '20px',
border: '1px solid gray'
}}
contentEditable
onBlur={this.handleBlur}
/>
<div
onClick={this.handleSubmit}
style={{backgroundColor:'#ddd'}}
>Submit</div>
</div>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById('container')
);
<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="container">
<!-- This element's contents will be replaced with your component. -->
</div>
In my code, the contentEditable div is imported from another file, and its own onBlur simply calls the handleBlur passed as a prop from my main form file. Is there something I am missing, and how can I fix this?
I actually solved this thanks to a similar non-issue that was posted on github: What happened was I had my component that I imported change size upon onBlur (it hides the character counter) and therefore the Buttons underneath it move between the onBlur and the potential onClick event, therefore they aren't underneath the mouse pointer by the time the onClick event would fire.
A very easy fix is to have my component be of fixed size, between focused and blurred states so that the other buttons don't move.

Cannot find ref on initial render

I just read in the official documents that componentDidUpdate isn't called on first render and I think maybe that's why dom isn't defined the first time this Component of mine is rendered.
This is a pop up modal that pops up when a page needs to be edited.
Is there any other way I can go about this?
componentDidUpdate() {
this.renderSingularForm();
}
renderSingularForm() {
let dom = ReactDOM.findDOMNode( this.refs.singularForm );
if ( this.props.pageObjToEdit && dom ) {
// DOESN'T GO HERE ON FIRST RENDER BECAUSE DOM IS NULL
createForm( window, dom, this.props.pageObjToEdit );
}
}
render() {
if ( this.props.pageObjToEdit ) {
return (
<div>
<div ref="singularForm" />
</div>
);
}
else {
return null;
}
}
This is an issue with how you are using refs. You shouldn't be using string refs anymore since they will be deprecated soon. Most people are now using an inline ref ( ref={ref => this.input = ref} ), but when you do that, the first time the component renders it will receive a null value. Then on the second render, the refs will be correctly assigned with the DOM element.
To get around this, you should supply a class method to the ref prop instead of an inline function.
Example:
This:
render() {
return (
...
<div ref="singularForm" />
...
)
}
Should be:
applyRef = ref => {
this.singularForm = ref;
}
render() {
return (
...
<div ref={this.applyRef} />
...
)
}
When you use a class method to apply a ref, it only gets called once when the actual element has been added to the dom, so you shouldn't get the initial null values anymore.
UPDATE 10/18:
You can now avoid this problem altogether using the new React.createRef to create your refs.
You can and should use the componentDidMount in order to safely get DOM elements or refs
From the DOCS:
componentDidMount() is invoked immediately after a component is
mounted. Initialization that requires DOM nodes should go here.
...
It can, however, be necessary for cases like modals and tooltips when
you need to measure a DOM node before rendering something that depends
on its size or position.
Also Note that you are using the old ref API.
You should use the new ref API
Running Example:
class App extends React.Component {
componentDidMount() {
console.log(this.myDiv.id);
}
render() {
return (
<div>
<div id="some-id" ref={ref => (this.myDiv = ref)}>some div</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<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="root"></div>

In React, Is it good practice to search for certain element in DOM?

Is it good to just specify className for element so i could find it later in the DOM through getElementsByClassName for manipulations?
Adding a class to find the DOM element? Sure you can do that, but refs are probably the better solution.
Manipulating the DOM element? That's an absolute no-go. The part of the DOM that is managed by React should not be manipulated my anything else but React itself.
If you come from jQuery background, or something similar, you will have the tendency to manipulate element directly as such:
<div class="notification">You have an error</div>
.notification {
display: none;
color: red;
}
.show {
display: block;
}
handleButtonClick(e) {
$('.notification').addClass('show');
}
In React, you achieve this by declaring what your elements (components) should do in different states of the app.
const Notification = ({ error }) => {
return error
? <div className="notification">You have an error</div>
: null;
}
class Parent extends React.Component {
state = { error: false };
render() {
return (
<div>
<Notification error={this.state.error} />
<button onClick={() => this.setState({ error: true })}>
Click Me
</button>
}
}
The code above isn't tested, but should give you the general idea.
By default, the state of error in Parent is false. In that state, Notification will not render anything. If the button is clicked, error will be true. In that state, Notification will render the div.
Try to think declaratively instead of imperatively.
Hope that helps.
When using React, you should think about how you can use state to control how components render. this.setState performs a rerender, which means you can control how elements are rendered by changing this.state. Here's a small example. I use this.state.show as a boolean to change the opacity of the HTML element.
constructor(props) {
super(props)
this.state = {
show: true
}
}
handleClick() {
this.setState({show: false})
}
render() {
const visibility = this.state.show ? 1 : 0
return (
<button style={{opacity: visibility} onClick={() => this.handleClick()}>
Click to make this button invisible
</button>
)
}

Add custom props to a custom component

I've built my own custom react-bootstrap Popover component:
export default class MyPopover extends Component {
// ...
render() {
return (
<Popover {...this.props} >
// ....
</Popover>
);
}
}
The component is rendered like so:
// ... my different class ...
render() {
const popoverExample = (
<MyPopover id="my-first-popover" title="My Title">
my text
</MyPopover >
);
return (
<OverlayTrigger trigger="click" placement="top" overlay={popoverExample}>
<Button>Click Me</Button>
</OverlayTrigger>
);
}
Now, I want to add custom props to MyPopover component like that:
my text
And to use the new props to set some things in the popover
for example -
<Popover {...this.props} className={this.getClassName()}>
{this.showTheRightText(this.props)}
</Popover>
but then I get this warning in the browser:
Warning: Unknown props popoverType on tag. Remove these props from the element.
Now, I guess that I can just remove the {...this.props} part and insert all the original props one by one without the custom props, but In this way I lose the "fade" effect and also it's an ugly way to handle this problem. Is there an easier way to do it?
Updated answer (React v16 and older):
As of React v16, you can pass custom DOM attributes to a React Component. The problem/warning generated is no longer relevant. More info.
Original answer (React v15):
The easiest solution here is to simply remove the extra prop before sending it to the Popover component, and there's a convenient solution for doing that.
export default class MyPopover extends Component {
// ...
render() {
let newProps = Object.assign({}, this.props); //shallow copy the props
delete newProps.popoverType; //remove the "illegal" prop from our copy.
return (
<Popover {...newProps} >
// ....
</Popover>
);
}
}
Obviously you can (and probably should) create that variable outside your render() function as well.
Basically you can send any props you want to your own component, but you'd have to "clean" it before passing it through. All react-bootstrap components are cleansed from "illegal" props before being passed as attributes to the DOM, however it doesn't handle any custom props that you may have provided, hence why you have to do your own bit of housekeeping.
React started throwing this warning as of version 15.2.0. Here's what the documentation says about this:
The unknown-prop warning will fire if you attempt to render a DOM element with a prop that is not recognized by React as a legal DOM attribute/property. You should ensure that your DOM elements do not have spurious props floating around.
[...]
To fix this, composite components should "consume" any prop that is intended for the composite component and not intended for the child component.
For further reading, check this page from the official react site.

onClick on div element won't pass target id

I have an issue in my React.js app with the target id not being passed if a onClick event is attached on a div element.
This is how I add the component:
<MenuOption title="Users" onOptionClick={this.onOptionClick}/>
This is the jsx-part of my MenuOption component:
<div id={title} className="row menu-option" onClick={onOptionClick}>
<p>{title}</p>
</div>
As you can see I'm simply assigning the prop title as the id and the onClick event.
Back in the parent component, implementation of the onClick method:
onSidebarOptionClick(e) {
console.log(e.target.id);
}
This does not seem to work since it always logs undefined.
However, if I add the id and the onClick event to my p element instead...
<div className="row menu-option">
<p id={title} onClick={onOptionClick}>{title}</p>
</div>
...it works just as expected, the correct id logs at every click.
So why is not the id passed if the click event is attached to a div?
I`m using ES6 and babel.
I should clarify that I'm using "dumb" components, declaring them like this:
const MenuOption = ({title, onOptionClick})
...which is why don't need to use {this.props.title} when using it.
Not sure as your Question missing constructor but try one of the following--
Method 1 (JSX):
<MenuOption title="Users" onOptionClick={this.onOptionClick.bind(this)}/>
Method 2 (es6):
constructor(props) {
super(props);
this.onOptionClick = this.onOptionClick.bind(this);
}
Method 3(es-next):
onSidebarOptionClick = (e) => {
console.log(e.target.id);
}
As you're passing onOptionClick as a prop to <MenuOption/>, it should be declared as this.porops.onOptionClick.
<MenuOption/> should look like this
<div id={title} className="row menu-option" onClick={thi.props.onOptionClick}>
<p>{title}</p>
</div>
Hope this helps!

Resources