How can I 'talk' with dom elements with react?
For example - I need to bind some actions with some js lib
Both approaches returns undefined for some reason
componentDidMount() {
const element1 = document.querySelector('.home-container')
const element2 = ReactDOM.findDOMNode(this);
// returns undefined, undefined
console.log(element1.length, element2.length);
}
render() {
return (
<div className="home-container">
...
</div>
)
}
But console.log(element1) returns html from render itself though
How can I work with them?
And what correct lifecycle method for this?
You use "refs":
<div ref={e => {this.div = el}} ...>...</div>
Once your component has rendered, with the above, its div property (you can use any name you want) will refer to the div element that was rendered.
Here's an example largely copied from the link above that focuses a text input when it's rendered:
class AutoFocusTextInput extends React.Component {
componentDidMount() {
this.textInput.focus();
}
render() {
return (
<input type="text"
ref={(input) => { this.textInput = input; }} />
);
}
}
ReactDOM.render(
<AutoFocusTextInput />,
document.getElementById("root")
);
<div id="root"></div>
<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>
Related
minimum reproducible example: https://codesandbox.io/s/react-hover-example-tu1eu?file=/index.js
I currently have a new element being rendered when either of 2 other elements are hovered over. But i would like to render different things based upon which element is hovered.
In the example below and in the codepen, there are 2 hoverable divs that are rendered; when they are hovered over, it changes the state and another div is rendered. I would like for the HoverMe2 div to render text "hello2". Currently, whether i hover hoverme1 or 2, they both just render the text "hello".
import React, { Component } from "react";
import { render } from "react-dom";
class HoverExample extends Component {
constructor(props) {
super(props);
this.handleMouseHover = this.handleMouseHover.bind(this);
this.state = {
isHovering: false
};
}
handleMouseHover() {
this.setState(this.toggleHoverState);
}
toggleHoverState(state) {
return {
isHovering: !state.isHovering
};
}
render() {
return (
<div>
<div
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me
</div>
<div
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me2
</div>
{this.state.isHovering && <div>hello</div>}
</div>
);
}
}
render(<HoverExample />, document.getElementById("root"));
You need to keep the state of item which you have hovered that's for sure
const { Component, useState, useEffect } = React;
class HoverExample extends Component {
constructor(props) {
super(props);
this.handleMouseHover = this.handleMouseHover.bind(this);
this.state = {
isHovering: false,
values: ['hello', 'hello2'],
value: 'hello'
};
}
handleMouseHover({target: {dataset: {id}}}) {
this.setState(state => {
return {
...state,
isHovering: !state.isHovering,
value: state.values[id]
};
});
}
render() {
return (
<div>
<div
data-id="0"
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me
</div>
<div
data-id="1"
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me2
</div>
{this.state.isHovering && <div>{this.state.value}</div>}
</div>
);
}
}
ReactDOM.render(
<HoverExample />,
document.getElementById('root')
);
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<div id="root"></div>
You can pass the context text as shown in example. This is working code:
import React, { Component } from "react";
import { render } from "react-dom";
// Drive this using some configuration. You can set based on your requirement.
export const HOVER_Hello1 = "Hello1";
export const HOVER_Hello2 = "Hello2";
class HoverExample extends Component {
constructor(props) {
super(props);
this.handleMouseHover = this.handleMouseHover.bind(this);
this.state = {
isHovering: false,
contextText: ""
};
}
handleMouseHover = (e, currentText) => {
this.setState({
isHovering: !this.state.isHovering,
contextText: currentText
});
}
toggleHoverState(state) {
//
}
render() {
return (
<div>
<div
onMouseEnter={e => this.handleMouseHover(e, HOVER_Hello1)}
onMouseLeave={e => this.handleMouseHover(e, HOVER_Hello1)}
>
Hover Me
</div>
<div
onMouseEnter={e => this.handleMouseHover(e, HOVER_Hello2)}
onMouseLeave={e => this.handleMouseHover(e, HOVER_Hello2)}
>
Hover Me2
</div>
{this.state.isHovering && <div>{this.state.contextText}</div>}
</div>
);
}
}
export default HoverExample;
If the whole point is about linking dynamically messages to JSX-element you're hovering, you may store that binding (e.g. within an object).
Upon rendering, you simply pass some anchor (e.g. id property of corresponding object) within a custom attribute (data-*), so that later on you may retrieve that, look up for the matching object, put linked message into state and render the message.
Following is a quick demo:
const { Component } = React,
{ render } = ReactDOM,
rootNode = document.getElementById('root')
const data = [
{id:0, text: 'Hover me', message: 'Thanks for hovering'},
{id:1, text: 'Hover me too', message: 'Great job'}
]
class HoverableDivs extends Component {
state = {
messageToShow: null
}
enterHandler = ({target:{dataset:{id:recordId}}}) => {
const {message} = this.props.data.find(({id}) => id == recordId)
this.setState({messageToShow: message})
}
leaveHandler = () => this.setState({messageToShow: null})
render(){
return (
<div>
{
this.props.data.map(({text,id}) => (
<div
key={id}
data-id={id}
onMouseEnter={this.enterHandler}
onMouseLeave={this.leaveHandler}
>
{text}
</div>
))
}
{
this.state.messageToShow && <div>{this.state.messageToShow}</div>
}
</div>
)
}
}
render (
<HoverableDivs {...{data}} />,
rootNode
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
As #CevaComic pointed out, you can do this with CSS. But if you want to use React, for example, because your actual problem is more complex, here is the answer.
You will need a way to tell apart the two elements. It could be done with some neat tricks, like setting an unique id to each element, passing a custom argument, or something else.
But I would advise against "cool tricks" as it's more difficult to understand what is going on, and the code is more prone to errors. I think the best way it to use a dumb approach of unique functions for unique elements.
Each onMouseEnter and onMouseLeave has to be an unique function (e.g. handleMouseHover1 and handleMouseHover2), and each of those functions need to control unique state (for example, isHovering1 and isHovering2). Then you have to render the element you want based on the state. Of course, for a real-world code, you will probably want to use more descriptive names to make the code more comprehensible. The full code would look something like this.
class HoverExample extends Component {
state = {
isHovering1: false,
isHovering2: false
};
handleMouseHover1 = () => {
this.setState(({ isHovering1 }) => ({ isHovering1: !isHovering1 }));
};
handleMouseHover2 = () => {
this.setState(({ isHovering2 }) => ({ isHovering2: !isHovering2 }));
};
render() {
const { isHovering1, isHovering2 } = this.state;
return (
<div>
<div
onMouseEnter={this.handleMouseHover1}
onMouseLeave={this.handleMouseHover1}
>
Hover Me1
</div>
<div
onMouseEnter={this.handleMouseHover2}
onMouseLeave={this.handleMouseHover2}
>
Hover Me2
</div>
{isHovering1 && <div>hello1</div>}
{isHovering2 && <div>hello2</div>}
</div>
);
}
}
Also, updated example: https://codesandbox.io/s/react-hover-example-rc3h0
Note: I have also edited the code to add some syntax sugar which exists with newer ECMAScript versions. Instead of binding the function, you can use the arrow function format, e.g. fn = () => { ... }. The arrow function means the this context is automatically bound to the function, so you don't have to do it manually. Also, you don't have to initialize this.state inside the constructor, you can define it as a class instance property. With those two things together, you do not need the constructor at all, and it makes the code a bit cleaner.
Is it a legit way to pass generated HTML in the render function of the component in JSX?
...
//get variables elsewhere
const input = <input type={inputType} ... />
return (
{input}
)
...
When I've tried to build it as a string, e.g. const input = '<input type="' + inputType'" + />' it was rendered as a plain text.
Actually, my return is:
return (
<div>{input}</div>
)
The code you've posted is perfectly fine other than the return (we'll get to that in a moment); you don't need or want to use a string instead.
Remember that JSX is just syntactic sugar for JavaScript code:
const input = <input type={inputType} />;
...is just a sugared version of React.createElement:
const input = React.createElement("input", { type: inputType });
It creates the element object, which you can certainly pass around between functions, and which you can render by returning it from render.
To do that return, you want simply:
return input;
Your return ({input}) won't work because you're trying to use the JSX syntax for inserting a JavaScript expression ({...}) outside JSX.
Live Example:
class Example extends React.Component {
getTheThing() {
const inputType = "text";
const input = <input type={inputType} />;
return input;
}
render() {
const input = this.getTheThing();
return input;
}
}
ReactDOM.render(
<Example />,
document.getElementById("root")
);
<div id="root"></div>
<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>
Re your edit:
Actually, my return is:
return (
<div>{input}</div>
)
That's fine (other than the missing ; — I don't care for relying on ASI), because you're using {...} in a JSX block (<div>...</div>).
Live Example:
class Example extends React.Component {
getTheThing() {
const inputType = "text";
const input = <input type={inputType} />;
return input;
}
render() {
const input = this.getTheThing();
return (
<div>{input}</div>
);
}
}
ReactDOM.render(
<Example />,
document.getElementById("root")
);
<div id="root"></div>
<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>
I'm trying to get an instance of the class ActionEditor So that I'd be able to use its methods later:
function render() {
const toRender = responseActions.map((actionInstance) => {
currentActionEditing=actionInstance;
return <li>{ actionInstance === expandedAction ? <ActionEditor id={actionInstance.title} action={getActionByKey(actionInstance.actionType)} instance={actionInstance} state={actionInstance} /> : <button onClick={createOnClick(actionInstance)}>{actionInstance.title}</button>}</li>;
});
ReactDOM.render(
<div>
<div>{toRender}</div>
<button style={styleButtonGenerate} onClick={onGenerateClick}>Generate</button>
</div>,
document.getElementById('root')
);
}
I've attempted to use it through an onClick method like so:
function onGenerateClick() {
var editor = document.getElementById(currentActionEditing.title);
editor.prototype = ActionEditor;
editor.methodIWantToUse();
}
But it always turns out to be null/undefined.
I understand that it's not the best example but it should be enough to demonstrate the issue.
Is there a way around this?
I think what you want here is to save a ref to the component so it can be accessed, see in the example below how the sayHi method is called from the parent component.
class MyComponent extends React.Component {
sayHi() {
console.log('hi');
}
render() {
return (<div>I'm a component!</div>)
}
}
class App extends React.Component {
render() {
// just a way to show how to access a child component method.
setTimeout(() => {
this.node.sayHi();
}, 1000)
return (<MyComponent ref={(node) => this.node = node}/>)
}
}
ReactDOM.render(<App />, document.querySelector("body"))
<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>
I asked a question on here earlier, but I think I should have been more specific. Say I have two components, which I call from a higher order component:
So my higher order component, containing these components, looks like this (or at least its render method):
<div>
<Navigation />
<View />
</div>
Now, Navigation /> contains a button. Whenever that button is clicked, I want to set the focus on an input field, which is in <View />.
If both things were in the very same component, I learned that I would do something like this:
<button onClick={() => {this.myInp.focus()}}>Focus Input</button>
<input type="text" ref={(ip) => this.myInp = ip} />
I am using Redux by the way, if this should make a difference.
Since they are descendants of the same element you can use a state element and function to change that state element in the parent div and pass down as props to View and Navigation.
constructor(){
super()
this.state = {
inputFocus:false;
}
this.setInputFocus = this.setInputFocus.bind(this)
}
setInputFocus(value){
this.setState({inputFocus:false});
}
Then in the render method of the div just pass them down as props
<div>
<Navigation setInputFocus={this.setInputFocus}/>
<View inputFocus={this.state.inputFocus}/>
</div>
In the Navigation
<button onClick={() => {this.props.setInputFocus(true)}}>Focus Input</button>
and in the View
<input type="text" ref={(ip) => this.myInp = ip}/>
and have this in the componentWillReceiveProps which detects when props change and runs actions then.
componentWillReceiveProps(nextProps){
if(nextProps.inputFocus)
this.myInp.focus()
}
So when the inputFocus changes in the parent it will fire the componentWillReceiveProps in the View element and focus the input.
Along with the refs, this problems also involves child to parent and parent to child interaction.
So what I am doing in the below snippet is form the Navigation component, I have to call the parent component since a component can't directly have access to its siblings. From the parent you can get access to the child component through refs and in turn you can also access the refs which are defined in the child component
See the snippet below
class App extends React.Component {
focusInput = () => {
this.myView.myInput.focus()
}
render() {
return (
<div>
<Navigation focusInput={this.focusInput}/>
<View ref={(view) => {this.myView = view}}/>
</div>
)
}
}
class Navigation extends React.Component {
render() {
return (
<button onClick={() => this.props.focusInput()}>Focus</button>
)
}
}
class View extends React.Component {
render() {
return (
<input type="text" ref={(ip) => this.myInput = ip}/>
)
}
}
ReactDOM.render(<App/>, 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>
In case you have multiple buttons in navigation thorugh which you want to focus multiple different inputs, you can do so in the following manner
class App extends React.Component {
focusInput = (ip) => {
this.myView[ip].focus()
}
render() {
return (
<div>
<Navigation focusInput={(val) => this.focusInput(val)}/>
<View ref={(view) => {this.myView = view}}/>
</div>
)
}
}
class Navigation extends React.Component {
render() {
return (
<div>
<button onClick={() => this.props.focusInput("myInput1")}>Focus1</button>
<button onClick={() => this.props.focusInput("myInput2")}>Focus2</button>
</div>
)
}
}
class View extends React.Component {
render() {
return (
<div>
<input type="text" ref={(ip) => this.myInput1 = ip}/>
<input type="text" ref={(ip) => this.myInput2 = ip}/>
</div>
)
}
}
ReactDOM.render(<App/>, 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>
I'm reading this file https://github.com/luispcosta/reddit_clone/blob/master/client/components/Main.jsx
and I don't get line 37, where does the this.props.children come from? I know you can pass props like
<Component name='alan'/>
and you can get name by doing this.props.name to get alan. But this props.children means?
In JSX expressions that contain both an opening tag and a closing tag,
the content between those tags is passed as a special prop:
props.children. There are several different ways to pass children:
https://facebook.github.io/react/docs/jsx-in-depth.html#children-in-jsx
For example:
<Main>
<p>This is a child element</p>
<button>So is this</button>
</Main>
this.props.children in Main will be an array of elements.
// Main.jsx
render() {
return <div>
{this.props.children}
</div>;
}
Would render
<div>
<p>This is a child element</p>
<button>So is this</button>
</div>
In react an element in JSX is represented as
const elem = (<div id="test"> Hi I am a div. </div>);
And there is another alternative way to represent the react element.
So similar element can be represented as -
var elem = React.createElement(
"div",
{ id: "test" },
" Hi I am a div. "
);
createElement has following signature :
React.createElement(component, props, ...children)
Above ...children means any nested JSX elements inside div.
Nested elements in itself can be another react-element and may be more than one of them.
const elem = (<div id="test"> <h1> Hi I am a div. </h1> </div>);
What is props.children?
The returned elem contains light weight object representation.
It has a field called props set. This props field contains custom attributes passed and reference to nested elements i.e child elements.
Now lets see how it is represented
const elem = (<div id="test">Hi I am a div.</div>);
console.dir(elem);
const elem1 = (<div id="test"> <h1> Hi I am a div. </h1> </div>);
console.dir(elem1);
<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>
From the logs we can see that props has a property called children. So each parent element contains immediate reference to its children.
"props": {
"id": "test",
"children": "Hi I am a div."
}
When can I use props.children?
When ever you want to get access to children elements in code.
There can be scenario like you want to get access to all of your nested elements.
A simplest example-
You have created a custom button like below -
class MyButton extends React.Component {
render() {
return <button> {this.props.children} </button>;
}
};
// What ever you pass insede <MyButton> and </MyButton>
// You can access it inside your MyButton component via {this.props.children}
const elem = (<MyButton> test1 </MyButton>);
ReactDOM.render(
elem,
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>
Another scenario could be you want to loop over your nested children and may be passing some other extra props or may just accessing them -
class MyButton extends React.Component {
render() {
return (
<button onClick={this.props.handleClick}>
{this.props.children}
</button>
);
}
};
class ButtonContainer extends React.Component {
handleClick = () => {
console.log("Clicked");
}
render() {
const childrenWithProps = React.Children.map(this.props.children,
(child) => {
return React.cloneElement(child, { handleClick: this.handleClick });
});
return (
<div>
{childrenWithProps}
</div>
);
}
};
const elem = (
<ButtonContainer>
<MyButton> test1 </MyButton>
<MyButton> test2 </MyButton>
<MyButton> test3 </MyButton>
</ButtonContainer>
);
ReactDOM.render(
elem,
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>
There are other scenarios as well with react-router e.g when route changes you want to get access to your children elements to render them on screen.
I have included working code and example where ever possible. I hope this helps you.
It is simple. I will explain by an example
Lets say you have created some component called ComponentDemo.
It is used like this:
<ComponentDemo>
// This component can receive some HTML tags here that will be passed as children props for example:
<div> Hello world</div>
</ComponentDemo>
Then,inside ComponentDemo render function you can use it as:
render(){
// lets use the HTML tags we passed as children here
{this.props.children}
}
what will happen?
ComponentDemo will render
<div> Hello world</div>
which is the value passed by props.children
You can check the API reference of react-router
So, it is just the api provided by react-router which is imported in https://github.com/luispcosta/reddit_clone/blob/master/client/routes.js.
const routes = (
<Route path="/" component={App}>
<Route path="groups" component={Groups} />
<Route path="users" component={Users} />
</Route>
)
class App extends React.Component {
render () {
return (
<div>
{/* this will be either <Users> or <Groups> */}
{this.props.children}
</div>
)
}
}