Dynamically creating refs without strings - javascript

EDIT: We're using React 16.2.0, which is relevant to the question (see this answer).
So far as I can tell, this is the accepted way to create a ref (at least for our version of react):
<div ref={(r) => { this.theRef = r; }}>Hello!</div>
And then it can be used something like this:
componentDidMount() {
if (this.theRef) {
window.addEventListener('scroll', this.handleScroll);
}
}
This works fine. However, if I want to create a dynamically named ref, say as part of a loop, how do I go about naming the ref?
Put in now obsolete terms, I would like something along these lines:
<div ref="{refName}">Hello!</div>
Thanks!

Try just:
<div ref={refName}>Hello!</div>

For a map you need a key, so maybe you could just use that key to map to an object? Like so:
this.myRefs = {}
doSomethingToRef = (key) => {
this.myRefs[key].doSomething()
}
return (
myValues.map(value => (
<div key={value.key} ref = {r => {this.myRefs[value.key] = r}}>{...}</div>
))
)

Use ref like this:
Define the refName inside the class constructor:
this.refName = React.createRef()
Assign the ref in your element:
<div ref={this.refName} id="ref-name">Hello!</div>
To access the refName, use current:
this.refName.current
Example:
if (this.refName.current.id == 'ref-name') {
window.addEventListener('scroll', this.handleScroll);
}
Update
As per your comment, to use ref in older version, you may use just like this:
<div ref={(el) => this.refName = el} id="ref-name">Hello!</div>
{/* notice double quote is not used */}
To access:
this.refs.refName
Example:
if (this.refs.refName.id == 'ref-name') {
window.addEventListener('scroll', this.handleScroll);
}
To do it in more better way, you may use callback pattern.

[short-id][1] might be a good candidate!
It has methods like:
ids.store('foo'); // "8dbd46"
ids.fetch('8dbd46'); // 'foo'
ids.fetch('8dbd46'); // undefined
ids.configure(Object conf)
$ npm install short-id
RefsCmp.js
var shortid = require('shortid');
const RefsList = (newrefs) = > (
{newrefs.map(item => (
<div ref={shortid.generate()}>Hello!</div>
))}
)
export default RefsList;

Related

Make two references to the same React element [duplicate]

I'm using the useHover() react hook defined in this recipe. The hook returns a ref and a boolean indicating whether the user is currently hovering over element identified by this ref. It can be used like this...
function App() {
const [hoverRef, isHovered] = useHover();
return (
<div ref={hoverRef}>
{isHovered ? 'Hovering' : 'Not Hovering'}
</div>
);
}
Now let's say that I want to use another (hypothetical) hook called useDrag which returns a ref and a boolean indicating whether the user is dragging the current element around the page. I want to use this on the same element as before like this...
function App() {
const [hoverRef, isHovered] = useHover();
const [dragRef, isDragging] = useDrag();
return (
<div ref={[hoverRef, dragRef]}>
{isHovered ? 'Hovering' : 'Not Hovering'}
{isDragging ? 'Dragging' : 'Not Dragging'}
</div>
);
}
This won't work because the ref prop can only accept a single reference object, not a list like in the example above.
How can I approach this problem so I can use multiple hooks like this on the same element? I found a package that looks like it might be what I'm looking for, but I'm not sure if I'm missing something.
A simple way to go about this is documented below.
Note: the ref attribute on elements takes a function and this function is later called with the element or node when available.
function App() {
const myRef = useRef(null);
return (
<div ref={myRef}>
</div>
);
}
Hence, myRef above is a function with definition
function(element){
// something done here
}
So a simple solution is like below
function App() {
const myRef = useRef(null);
const anotherRef = useRef(null);
return (
<div ref={(el)=> {myRef(el); anotherRef(el);}}>
</div>
);
}
A React ref is really nothing but a container for some mutable data, stored as the current property. See the React docs for more details.
{
current: ... // my ref content
}
Considering this, you should be able to sort this out by hand:
function App() {
const myRef = useRef(null);
const [hoverRef, isHovered] = useHover();
const [dragRef, isDragging] = useDrag();
useEffect(function() {
hoverRef.current = myRef.current;
dragRef.current = myRef.current;
}, [myRef.current]);
return (
<div ref={myRef}>
{isHovered ? 'Hovering' : 'Not Hovering'}
{isDragging ? 'Dragging' : 'Not Dragging'}
</div>
);
}

In React 17 jquery issue?

I'm using React. I need jquery functionality. I solved this problem with npm install jquery. But is such a use correct? I have never seen a user before. Will I encounter a problem?
It's quite strange to mix a framework like React with jQuery. Not completely unheard of, but it's almost never the right way to do things.
Here, you can achieve the same effect more properly by
using addEventListener instead of jQuery
using useEffect to add the listener once (and to remove it once the component unmounts)
Setting state which changes the nav's class via JSX instead of doing it with DOM manipulation
const Header = () => {
const [shrink, setShrink] = useState(false);
useEffect(() => {
const handler = () => {
setShrink(window.scrollY > 50);
}
document.addEventListener('scroll', handler);
return () => document.removeEventListener('scroll', handler);
}, []);
const navClass = (shrink ? 'shrink ' : '') + 'navbar navbar-expand-lg'; // etc
return (
<header>
<nav className={navClass}>
...

How to pass method from one component to another without using Arrow Function?

In the below code snippet, I have to pass the method 'handleChange' to another component 'TodoList'. I have tried binding the method, but it didn't solve the problem.
import React from "react";
import TodoList from "./TodoItem";
import "./TodoStyles.css";
import {list} from "./TodoList"
class TodoApp extends React.Component {
constructor(){
super();
this.state = {
todos : list
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(id){
console.log("Changed",id);
}
render(){
const todoArr = this.state.todos.map(
function(i){
return <TodoList key = {i.id} arr = {i} handleChange = {this.handleChange}/>
}
);
return(
<div className = 'todo-list'>
{todoArr}
</div>
)
}
}
export default TodoApp
But using an arrow function instead of a regular function,it solves the issue.
const todoArr = this.state.todos.map(
(i) => {
return <TodoList key = {i.id} arr = {i} handleChange = {this.handleChange}/>
}
);
Error Message
My question is, can we pass the function "handleChange" to other component using the regular function?
I am working in ES6 and react for the past 1 week, I have tried to search for a solution online, but didn't get any that can solve my problem. So, please be gentle.
I am not sure why you cannot use arrow functions. They have been supported in all browsers for years - and tools like Babel (which you can easily configure bundlers like Webpack to use) can convert your code to ES5 automatically if you really have to support something like IE.
However, if you really can't, you can just use the old "lexical this" trick that arrow functions were designed as a substitute for:
const self = this;
const todoArr = this.state.todos.map(
function(i){
return <TodoList key = {i.id} arr = {i} handleChange = {self.handleChange}/>
}
);
Note that this.handleChange = this.handleChange.bind(this) in the constructor doesn't directly help here. (Although you do still need it with the above code to make it work.) It's usually used so you can pass this.handleChange as an event handler and have it use the correct this - here, the function inside map already uses its own this so putting this.handleChange in the event handler won't refer to the correct thing, even if you bind.
Array.prototype.map() takes a second argument to set what this refers to in the mapping function, so pass this as the second argument to preserve the current context. [*]
You can try this:
const todoArr = this.state.todos.map(
function(i){
return <TodoList key = {i.id} arr = {i} handleChange = {this.handleChange}/>
}, this
);
[*] https://stackoverflow.com/a/30148997/1927991

ReactJS get refs/class/id from inside a loop/map to be used in JQuery

Can I get reference of class or id or refs of a element which is inside a map function?
componentDidMount(){
console.log(ReactDOM.findDOMNode(this.refs.items));
}
render(){
var loop = Object.keys(this.state.posts).map((key) => {
var post = this.state.posts[key];
return(
<div key={key} className="hakuna-matata" ref="items">
...
</div>
)
}
return {loop}
}
I would like to access ref=items or class=hakuna-matata array or object in jQuery
Can anyone help? Please
Well, so the content of your loop is an array. Collapse the array to a string to get the whole HTML Code.
Second, the question is where do you wish to have jQuery work with the HTML Code.
As long as you are in the render method the code is not in the DOM Tree. So you have to let jQuery parse the code: var myJqueryNode = $(loop.join());.
If you are in some kind of event handler in React after the rendering has been performed you can easily query the DOM Tree like: $('.hakuna-matata');
You may do not want to put the ref inside the loop because it causes duplication. Instead you can do this:
handlePost = (e, post) => {
//do whatever to post here...
//do whatever to e here...
}
render(){
const loop = Object.keys(this.state.posts).map((key) => {
const post = this.state.posts[key];
return(
<div key={key} className="hakuna-matata"
onClick={(e) => {this.handlePost(e, post}}>
...
</div>
);
}
return {loop}
}
Upon clicking the post, pass the post object to the method handlePost and you can do whatever to it without using refs. You can also pass e or event and access the event object.

React elements and fat arrow functions

In the Redux examples, the syntax used is:
const App = () => (
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
)
I was toying around with a new example app and mistyped the above code with curly brackets instead of parentheses like so:
const App = () => {
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
}
I console logged both of the following and the result seemed to be the same. My question is what is the difference between these 2 and why does React like the parentheses but not the curly brackets?
TL;DR
Your first example is more or less equivalent to:
var App = function() { return <div>...</div>; };
Your second is more or less equivalent to:
var App = function() { <div>...</div>; };
React is probably complaining that nothing is being returned in the second example.
Slightly Longer Version
Let's take React out of the equation. In es6 you can create a fat arrow function like this:
const getWord = () => {
return 'unicorn';
}
And we're given a shortcut to do the same thing with less code:
const getWord = () => 'unicorn';
unicorn is returned even though you don't ever explicitly type return anywhere.
In your first example, you wrapped your JSX in parenthesis. The equivalent in our simple example is:
const getWord = () => ('unicorn');
or this
const getWord = () => (
'unicorn'
);
The last four examples are equivalent. Hope that helps!

Categories

Resources