What is the difference between forwardingRef vs callback refs in React? - reactjs

As per the React.js official documentation, the below code is an example of callback refs.
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
componentDidMount(props) {
//Here, this.inputElement in Parent will be set to the DOM node corresponding to the element in the CustomTextInput
console.log(this.inputElement);
}
render() {
return (
<CustomTextInput
inputRef={el => this.inputElement = el}
/>
);
}
}
Here, this.inputElement in Parent will be set to the DOM node corresponding to the element in the CustomTextInput.
In case of forwarding ref, as per the official document,
const FancyButton = React.forwardRef((props, ref) => {
return (
<button ref={ref} className="FancyButton" data-name="My button">
{props.children}
</button>
);
});
//Parent Component
class FancyButtonWrapper extends React.Component {
constructor(props) {
super(props);
this.buttonRef = React.createRef();
}
componentDidMount(props) {
//Here this.ref will point to button element. Because of this reason, ref.current will give the value of button.
console.log(this.buttonRef.current.getAttribute("data-name"));
}
render() {
return (
//Here we are passing the ref to the child component.
<FancyButton ref={this.buttonRef} data-attr="Hello">
Click me!{" "}
</FancyButton>
);
}
}
Here, in this case, this.ref will point to the button element. Because of this reason, ref.current will give the value of the button.
Is there any difference between forwardRef and callbackRefs? We can access the child node's reference from parent in both of these cases.

I am not an expert but here are something to think about:
- callback refs are used when we need to dynamically set refs.
- Forward refs are commonly used when access to child refs are needed.

Well for the difference between the use of forwardingRef vs callback ref is in the HOC.
if you pass ref prop to HOC then inside HOC you cannot further pass it down to the enclosing component(which HOC wraps) since the props attributes does not store the ref inside it. ref is not a key ,see here: https://reactjs.org/docs/forwarding-refs.html 
so apart form this use case they work in same way.
Hope that helps !!

The difference is in the case of the ref callback, you can run side-effects when the ref changes. If you use useRef, you can access ref at any time but you will not know when it is set, or run a useEffect with the ref as a dependency
Callback refs have more control - they allow you to for example set a state when the ref is set, which will rerender the component when the component mounts, and you can use the ref node to do whatever you need.
In short - generally use useRef as it is the simplest. But ref callbacks can give you more control when needed

Related

Difference between `ref` and `innerRef` in ReactJS

I am using two class components where I have a method that I am invoking from the parent component. and for this reason, I have to create two references using React.createRef(). But the problem is one component allows me with ref attribute and another innerRef attribute. So I want to know what is the difference.
class X extends Component {
constructor(props) {
super(props);
this.xRef = React.createRef();
this.yRef = React.createRef();
}
render() {
return (
<Xcomponent
classes={classes}
user={user}
ref={this.xRef}
/>
<Ycomponent
classes={classes}
user={user}
innerRef={this.xRef}
/>
)
}
}
innerRef is a component instance property, while ref is a component instance attribute:
When the ref attribute is a callback function, the function receives the underlying DOM element or class instance.
// Access reference within the parent component: this.xRef.current
// Access property within the component: this.props.innerRef.current
<Ycomponent ref={this.xRef} innerRef={this.xRef} />
// coolRef is an ordinary component property, you can name it as you like
<Ycomponent ref={this.xRef} coolRef={this.xRef} />
// access with this.props.coolRef.current within the component.
In conclusion, the custom component Ycomponent defines the property innerRef for the developer to pass a reference with it.
For more info see related question: Why, exactly, do we need React.forwardRef?

pass ref to a class component with React.cloneElement and render prop

I'm writing a component that handle some internal state according to a ref of it's child (a mouse event related to that child's ref for example).
This component is using a render-prop to pass on the relevant piece of state to it's child, and render the child with the ref attached via React.cloneElement util.
The problem is that when the child is a class component, for some reason the ref is not available, and i can't find a way to render it as it's a react element object with a type of function (after i clone it of course).
But if the child is just a DOM node like a div for example, it is working as expected.
My work-around is to check the type of the child, and if it is a type of function I'll wrap the cloned element with my own div, if it's just a dom node then render as is.
However, i would like to not wrap the child with an extra div as i don't want to add unnecessary DOM nodes.
Here is a basic code example, most code removed for brevity:
The Parent component:
class Parent extends Component {
attachRef = node => {
this.ref = node;
}
render() {
const { render } = this.props;
const { someValue } = this.state;
const Child = render(someValue);
const WithRef = React.cloneElement(Child, {
ref: this.attachRef
});
if (typeof WithRef.type === 'string') { // node element
return WithRef;
}
else if (typeof WithRef.type === 'function') {
// this is a react element object.. not sure how to render it
// return ?
} else {
// need to find a way to render without a wrapping div
return (
<div ref={this.attachRef}>{Child}</div>
);
}
}
}
The usage:
class App extends Component {
render() {
return (
<div>
<Parent render={someValue => <div> {someValue}</div>} />
<Parent render={someValue => <Menu someValue={someValue} />} />
</div>
);
}
}
When i render regular DOM nodes like the first example it works fine, when i try to render the Menu (which is a class component) it doesn't work as mentioned above.
I had almost an identical issue.
i chose to use findDOMNode from react-dom, you can see the full solution in react-external-click.
Although the warning notes:
findDOMNode is an escape hatch used to access the underlying DOM node.
In most cases, use of this escape hatch is discouraged because it
pierces the component abstraction.
findDOMNode only works on mounted components (that is, components that
have been placed in the DOM). If you try to call this on a component
that has not been mounted yet (like calling findDOMNode() in render()
on a component that has yet to be created) an exception will be
thrown.
findDOMNode cannot be used on functional components.
I think this is the better solution for this particular challenge.
It let's you be "transparent" to the consumer, while being able to target the component in the DOM.
Ok here it is, grabbing the ref:
componentDidMount() {
this.ref = findDOMNode(this);
// some logic ...
}
this is how i use a render function with no wrapper of my own:
render() {
const { children, render } = this.props;
const { clickedOutside } = this.state;
const renderingFunc = render || children;
if (typeof renderingFunc === 'function') {
return renderingFunc(clickedOutside);
} else {
return null
}
}
}

Lifting up state in React

say i have this React Class. This is NOT my main component that I'm rendering. how can i pass the state i set in here UPWARDS to the parent component.
class Player extends React.Component {
constructor(props) {
super(props);
this.state = {
playerOneName: ''
}
this.selectPlayerOne = this.selectPlayerOne.bind(this);
}
selectPlayerOne(e, data) {
this.setState({playerOneName: data.value})
}
render() {
let players = [];
this.props.players.map((player) => {
players.push({text: player.name, value: player.name})
})
return (
<div className="playersContainer">
<div className="players">
<Dropdown onChange={this.selectPlayerOne} placeholder='Select Player One' fluid selection options={players} />
</div>
</div>
)
}
}
when I say parent component i mean the class that is going to display player like so:
<Player />
I.e. how can I make this.state.playerOneName available to the parent?
Hey so the point here is that you are basically passing in, from your parent component into your child component, a prop which is a function.
In parent:
handler(newValue){
this.setState({key: newValue})
}
render() {
return(
<Child propName={handler.bind(this)}>
)
}
When a change takes place in your child component, you call the function and pass in the new value as an input. This way you are calling the function in your child component, and making a change to the state of the parent component.
In your case you want to, instead of setting the state of playerOneName, pass in a function from the parent of this class and do something like this.props.functionFromParent(playerOneName) in your 'selectPlayOne' function.
It is true that the flux pattern is unidirectional, however when you're dealing with smart and dumb components you will see that data is passed from a dumb component to a smart component in this way. It is perfectly acceptable React form!
"Lifting state up" for React means that you should replace data from your child component to parent component state and pass data for presentation to child component by props.
You can do something like this to achieve your goal. Create a parent component which hold playerOneName as its state. Pass it as a prop to child component and with that also a function that changes the playerOneName whenever it is changed in the child component.
class Parent extends Component {
constructor(props){
this.state = {
playerOneName: ''
}
}
render() {
return(
<Child
playerOneName={this.state.playerOneName}
onPlayerOneNameChange={(playerOneName) => this.setState({playerOneName})}
/>
);
}
}
Use this function like this in child component to change the name of playerOneName in Parent component, like this your child component is only displaying the value of the playerOneName all the changes are done in Parent component only.
class Child = props => {
const { playerOneName, onPlayerOneNameChange } = props;
return (
<TextInput
value={playerOneName}
onChangeText={(playerOneName) => onPlayerOneNameChange(playerOneName)}
/>
);
}
By this you can use updated playerOneName in your Parent component whenever you like by using this.state.playerOneName

React/redux child update child (<select> update <select>)

I have container, binded with connect() to my store.
// GetActiveAccount.jsx
const VisibleActiveAccountsList = connect(
mapStateToProps,
mapDispatchToProps
)(ActiveAccountsSelector)
ActiveAccountsSelector is the presentational component. Inside this presentational component I load two child presentational components which are only
//activeAccountSelector render method
return(
<div>
<GatewaySelector gateways={gateways} onSelectGateway={ (e) => {
console.log(e.target.value);
}
}/>
<PairSelector gateway={gateways[0]} onSelectPair={ (e) => {
console.log(e.target.value);
}
}/>
</div>
)
What I want is that the selected value on gatewaySelector determine the value passed to PairSelector and update it when changed.
What is the right way to do that ? Going through actions seems way overkill for a simple select change update.
I guess I have to manager that in the parent (activeAccountSelector) but how ? It seems not advised to change state without going through the whole process (actions,reducers ...etc) shall I do that changing parents props ?
Yes. You have to manage that in state of the parent component. Simply, you can change set the value for gateway property of PairSelector from parent's state and change the state when gateway change in GatewaySelector.
Redux suggest to using react state for the state that doesn't matter to the app globally. Read this comment from the Redux author for more information.
class ActiveAccountSelector extends React.Component{
contructor(props){
super(props);
this.state = { selectedGateway: null};
this.onSelectGateway = this.onSelectGateway.bind(this);
}
onSelectGateway(e){
this.setState({ selectedGateway: e.target.value });
}
render(){}
....
return(
<div>
<form>
<GatewaySelector gateways={gateways} onSelectGateway={ this.onSelectGateway}
}/>
<PairSelector gateway={this.state.selectedGateway} onSelectPair={ (e) => {
console.log(e.target.value);
}}/>
</form>
</div>
);
}
}

React – the right way to pass form element state to sibling/parent elements?

Suppose I have a React class P, which renders two child classes, C1 and C2.
C1 contains an input field. I'll refer to this input field as Foo.
My goal is to let C2 react to changes in Foo.
I've come up with two solutions, but neither of them feels quite right.
First solution:
Assign P a state, state.input.
Create an onChange function in P, which takes in an event and sets state.input.
Pass this onChange to C1 as a props, and let C1 bind this.props.onChange to the onChange of Foo.
This works. Whenever the value of Foo changes, it triggers a setState in P, so P will have the input to pass to C2.
But it doesn't feel quite right for the same reason: I'm setting the state of a parent element from a child element. This seems to betray the design principle of React: single-direction data flow.
Is this how I'm supposed to do it, or is there a more React-natural solution?
Second solution:
Just put Foo in P.
But is this a design principle I should follow when I structure my app—putting all form elements in the render of the highest-level class?
Like in my example, if I have a large rendering of C1, I really don't want to put the whole render of C1 to render of P just because C1 has a form element.
How should I do it?
So, if I'm understanding you correctly, your first solution is suggesting that you're keeping state in your root component? I can't speak for the creators of React, but generally, I find this to be a proper solution.
Maintaining state is one of the reasons (at least I think) that React was created. If you've ever implemented your own state pattern client side for dealing with a dynamic UI that has a lot of interdependent moving pieces, then you'll love React, because it alleviates a lot of this state management pain.
By keeping state further up in the hierarchy, and updating it through eventing, your data flow is still pretty much unidirectional, you're just responding to events in the Root component, you're not really getting the data there via two way binding, you're telling the Root component that "hey, something happened down here, check out the values" or you're passing the state of some data in the child component up in order to update the state. You changed the state in C1, and you want C2 to be aware of it, so, by updating the state in the Root component and re-rendering, C2's props are now in sync since the state was updated in the Root component and passed along.
class Example extends React.Component {
constructor (props) {
super(props)
this.state = { data: 'test' }
}
render () {
return (
<div>
<C1 onUpdate={this.onUpdate.bind(this)}/>
<C2 data={this.state.data}/>
</div>
)
}
onUpdate (data) { this.setState({ data }) }
}
class C1 extends React.Component {
render () {
return (
<div>
<input type='text' ref='myInput'/>
<input type='button' onClick={this.update.bind(this)} value='Update C2'/>
</div>
)
}
update () {
this.props.onUpdate(this.refs.myInput.getDOMNode().value)
}
})
class C2 extends React.Component {
render () {
return <div>{this.props.data}</div>
}
})
ReactDOM.renderComponent(<Example/>, document.body)
Having used React to build an app now, I'd like to share some thoughts to this question I asked half a year ago.
I recommend you to read
Thinking in React
Flux
The first post is extremely helpful to understanding how you should structure your React app.
Flux answers the question why should you structure your React app this way (as opposed to how to structure it). React is only 50% of the system, and with Flux you get to see the whole picture and see how they constitute a coherent system.
Back to the question.
As for my first solution, it is totally OK to let the handler go the reverse direction, as the data is still going single-direction.
However, whether letting a handler trigger a setState in P can be right or wrong depending on your situation.
If the app is a simple Markdown converter, C1 being the raw input and C2 being the HTML output, it's OK to let C1 trigger a setState in P, but some might argue this is not the recommended way to do it.
However, if the app is a todo list, C1 being the input for creating a new todo, C2 the todo list in HTML, you probably want to handler to go two level up than P -- to the dispatcher, which let the store update the data store, which then send the data to P and populate the views. See that Flux article. Here is an example: Flux - TodoMVC
Generally, I prefer the way described in the todo list example. The less state you have in your app the better.
Five years later with introduction of React Hooks there is now much more elegant way of doing it with use useContext hook.
You define context in a global scope, export variables, objects and functions in the parent component and then wrap children in the App in a context provided and import whatever you need in child components. Below is a proof of concept.
import React, { useState, useContext } from "react";
import ReactDOM from "react-dom";
import styles from "./styles.css";
// Create context container in a global scope so it can be visible by every component
const ContextContainer = React.createContext(null);
const initialAppState = {
selected: "Nothing"
};
function App() {
// The app has a state variable and update handler
const [appState, updateAppState] = useState(initialAppState);
return (
<div>
<h1>Passing state between components</h1>
{/*
This is a context provider. We wrap in it any children that might want to access
App's variables.
In 'value' you can pass as many objects, functions as you want.
We wanna share appState and its handler with child components,
*/}
<ContextContainer.Provider value={{ appState, updateAppState }}>
{/* Here we load some child components */}
<Book title="GoT" price="10" />
<DebugNotice />
</ContextContainer.Provider>
</div>
);
}
// Child component Book
function Book(props) {
// Inside the child component you can import whatever the context provider allows.
// Earlier we passed value={{ appState, updateAppState }}
// In this child we need the appState and the update handler
const { appState, updateAppState } = useContext(ContextContainer);
function handleCommentChange(e) {
//Here on button click we call updateAppState as we would normally do in the App
// It adds/updates comment property with input value to the appState
updateAppState({ ...appState, comment: e.target.value });
}
return (
<div className="book">
<h2>{props.title}</h2>
<p>${props.price}</p>
<input
type="text"
//Controlled Component. Value is reverse vound the value of the variable in state
value={appState.comment}
onChange={handleCommentChange}
/>
<br />
<button
type="button"
// Here on button click we call updateAppState as we would normally do in the app
onClick={() => updateAppState({ ...appState, selected: props.title })}
>
Select This Book
</button>
</div>
);
}
// Just another child component
function DebugNotice() {
// Inside the child component you can import whatever the context provider allows.
// Earlier we passed value={{ appState, updateAppState }}
// but in this child we only need the appState to display its value
const { appState } = useContext(ContextContainer);
/* Here we pretty print the current state of the appState */
return (
<div className="state">
<h2>appState</h2>
<pre>{JSON.stringify(appState, null, 2)}</pre>
</div>
);
}
const rootElement = document.body;
ReactDOM.render(<App />, rootElement);
You can run this example in the Code Sandbox editor.
The first solution, with keeping the state in parent component, is the correct one. However, for more complex problems, you should think about some state management library, redux is the most popular one used with react.
I'm surprised that there are no answers with a straightforward idiomatic React solution at the moment I'm writing. So here's the one (compare the size and complexity to others):
class P extends React.Component {
state = { foo : "" };
render(){
const { foo } = this.state;
return (
<div>
<C1 value={ foo } onChange={ x => this.setState({ foo : x })} />
<C2 value={ foo } />
</div>
)
}
}
const C1 = ({ value, onChange }) => (
<input type="text"
value={ value }
onChange={ e => onChange( e.target.value ) } />
);
const C2 = ({ value }) => (
<div>Reacting on value change: { value }</div>
);
I'm setting the state of a parent element from a child element. This seems to betray the design principle of React: single-direction data flow.
Any controlled input (idiomatic way of working with forms in React) updates the parent state in its onChange callback and still doesn't betray anything.
Look carefully at C1 component, for instance. Do you see any significant difference in the way how C1 and built-in input component handle the state changes? You should not, because there is none. Lifting up the state and passing down value/onChange pairs is idiomatic for raw React. Not usage of refs, as some answers suggest.
More recent answer with an example, which uses React.useState
Keeping the state in the parent component is the recommended way. The parent needs to have an access to it as it manages it across two children components. Moving it to the global state, like the one managed by Redux, is not recommended for same same reason why global variable is worse than local in general in software engineering.
When the state is in the parent component, the child can mutate it if the parent gives the child value and onChange handler in props (sometimes it is called value link or state link pattern). Here is how you would do it with hooks:
function Parent() {
var [state, setState] = React.useState('initial input value');
return <>
<Child1 value={state} onChange={(v) => setState(v)} />
<Child2 value={state}>
</>
}
function Child1(props) {
return <input
value={props.value}
onChange={e => props.onChange(e.target.value)}
/>
}
function Child2(props) {
return <p>Content of the state {props.value}</p>
}
The whole parent component will re-render on input change in the child, which might be not an issue if the parent component is small / fast to re-render. The re-render performance of the parent component still can be an issue in the general case (for example large forms). This is solved problem in your case (see below).
State link pattern and no parent re-render are easier to implement using the 3rd party library, like Hookstate - supercharged React.useState to cover variety of use cases, including your's one. (Disclaimer: I am an author of the project).
Here is how it would look like with Hookstate. Child1 will change the input, Child2 will react to it. Parent will hold the state but will not re-render on state change, only Child1 and Child2 will.
import { useStateLink } from '#hookstate/core';
function Parent() {
var state = useStateLink('initial input value');
return <>
<Child1 state={state} />
<Child2 state={state}>
</>
}
function Child1(props) {
// to avoid parent re-render use local state,
// could use `props.state` instead of `state` below instead
var state = useStateLink(props.state)
return <input
value={state.get()}
onChange={e => state.set(e.target.value)}
/>
}
function Child2(props) {
// to avoid parent re-render use local state,
// could use `props.state` instead of `state` below instead
var state = useStateLink(props.state)
return <p>Content of the state {state.get()}</p>
}
PS: there are many more examples here covering similar and more complicated scenarios, including deeply nested data, state validation, global state with setState hook, etc. There is also complete sample application online, which uses the Hookstate and the technique explained above.
You should learn Redux and ReactRedux library.It will structure your states and props in one store and you can access them later in your components .
With React >= 16.3 you can use ref and forwardRef, to gain access to child's DOM from its parent. Don't use old way of refs anymore.
Here is the example using your case :
import React, { Component } from 'react';
export default class P extends React.Component {
constructor (props) {
super(props)
this.state = {data: 'test' }
this.onUpdate = this.onUpdate.bind(this)
this.ref = React.createRef();
}
onUpdate(data) {
this.setState({data : this.ref.current.value})
}
render () {
return (
<div>
<C1 ref={this.ref} onUpdate={this.onUpdate}/>
<C2 data={this.state.data}/>
</div>
)
}
}
const C1 = React.forwardRef((props, ref) => (
<div>
<input type='text' ref={ref} onChange={props.onUpdate} />
</div>
));
class C2 extends React.Component {
render () {
return <div>C2 reacts : {this.props.data}</div>
}
}
See Refs and ForwardRef for detailed info about refs and forwardRef.
The right thing to do is to have the state in the parent component, to avoid ref and what not
An issue is to avoid constantly updating all children when typing into a field
Therefore, each child should be a Component (as in not a PureComponent) and implement shouldComponentUpdate(nextProps, nextState)
This way, when typing into a form field, only that field updates
The code below uses #bound annotations from ES.Next babel-plugin-transform-decorators-legacy of BabelJS 6 and class-properties (the annotation sets this value on member functions similar to bind):
/*
© 2017-present Harald Rudell <harald.rudell#gmail.com> (http://www.haraldrudell.com)
All rights reserved.
*/
import React, {Component} from 'react'
import {bound} from 'class-bind'
const m = 'Form'
export default class Parent extends Component {
state = {one: 'One', two: 'Two'}
#bound submit(e) {
e.preventDefault()
const values = {...this.state}
console.log(`${m}.submit:`, values)
}
#bound fieldUpdate({name, value}) {
this.setState({[name]: value})
}
render() {
console.log(`${m}.render`)
const {state, fieldUpdate, submit} = this
const p = {fieldUpdate}
return (
<form onSubmit={submit}> {/* loop removed for clarity */}
<Child name='one' value={state.one} {...p} />
<Child name='two' value={state.two} {...p} />
<input type="submit" />
</form>
)
}
}
class Child extends Component {
value = this.props.value
#bound update(e) {
const {value} = e.target
const {name, fieldUpdate} = this.props
fieldUpdate({name, value})
}
shouldComponentUpdate(nextProps) {
const {value} = nextProps
const doRender = value !== this.value
if (doRender) this.value = value
return doRender
}
render() {
console.log(`Child${this.props.name}.render`)
const {value} = this.props
const p = {value}
return <input {...p} onChange={this.update} />
}
}
The concept of passing data from parent to child and vice versa is explained.
import React, { Component } from "react";
import ReactDOM from "react-dom";
// taken refrence from https://gist.github.com/sebkouba/a5ac75153ef8d8827b98
//example to show how to send value between parent and child
// props is the data which is passed to the child component from the parent component
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
fieldVal: ""
};
}
onUpdateParent = val => {
this.setState({
fieldVal: val
});
};
render() {
return (
// To achieve the child-parent communication, we can send a function
// as a Prop to the child component. This function should do whatever
// it needs to in the component e.g change the state of some property.
//we are passing the function onUpdateParent to the child
<div>
<h2>Parent</h2>
Value in Parent Component State: {this.state.fieldVal}
<br />
<Child onUpdate={this.onUpdateParent} />
<br />
<OtherChild passedVal={this.state.fieldVal} />
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.state = {
fieldValChild: ""
};
}
updateValues = e => {
console.log(e.target.value);
this.props.onUpdate(e.target.value);
// onUpdateParent would be passed here and would result
// into onUpdateParent(e.target.value) as it will replace this.props.onUpdate
//with itself.
this.setState({ fieldValChild: e.target.value });
};
render() {
return (
<div>
<h4>Child</h4>
<input
type="text"
placeholder="type here"
onChange={this.updateValues}
value={this.state.fieldVal}
/>
</div>
);
}
}
class OtherChild extends Component {
render() {
return (
<div>
<h4>OtherChild</h4>
Value in OtherChild Props: {this.props.passedVal}
<h5>
the child can directly get the passed value from parent by this.props{" "}
</h5>
</div>
);
}
}
ReactDOM.render(<Parent />, document.getElementById("root"));

Resources