I am using refs for access child component
<MyComponent
ref='_my_refs'
...
/>
and call them
this.refs._my_refs.scrollToTop();
I get the error below
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
You need to wrap MyComponent around React.forwardRef()
e.g.
const MyComponent = React.forwardRef((props, ref) => {
return (
<View ref={ref}> // using the ref
// your component
</View>
})
Also, ref='_my_refs' doesn't work because it's a legacy ref, you should use React.createRef() for class components or useRef for functional component.
You can check for more details in the docs.
e.g.
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this._my_refs = React.createRef();
}
render(){
return (
// ...
<MyComponent
ref={this._my_refs}
...
/>
)
}
}
OR
const ParentComponent = props => {
const myRef = React.useRef()
return (
// ...
<MyComponent
ref={myRef}
...
/>
)
}
If you pass a ref to a functional component and it isn't wrapped around React.forwardRef, it will give you the error
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
And this means MyComponent is a functional component and isn't wrapped around React.forwardRef.
Related
Are those 2 components below identical? What is the purpose is I'm trying to do a wrapper to customize a component.
does
const myComponent = (props) => {
return (
<OtherComponent {...props} />
)
}
and
class myComponent extends Component {
const { ...rest } = this.props
render() {
return <OtherComponent {...rest} />
}
}
{...props}
identical to
const { ...rest } = this.props
?
Yes, both ways of writing it are identical. Every key/value pair in this.props will end up in the rest variable.
Instead of writing const { ...rest } = this.props you can use this.props directly instead.
const MyComponent1 = (props) => {
return (
<OtherComponent {...props} />
)
}
class MyComponent2 extends Component {
render() {
return <OtherComponent {...this.props} />
}
}
Yes, they are identical. At first {...props} is the use of spread operator to copy the props object and pass it to the other component.
In case of const {...rest} = this.props, spread operator is used with Destructuring assignment. Destructuring syntax allows to extract or unpack objects or arrays into bunch of variables.
Code:
let obj={a:"1",b:"2",c:"3"}
let {a,...rest}=obj
console.log(a)
console.log(rest)
Output:
1
{b:"2",c:"3"}
Here, a is extracted from obj and is assigned to variable named a and rest of the object is assigned to variable named rest.
So it means doing const {..rest} = this.props and passing it to the other component results the same as doing {...props}.
There are two types of components in react
Stateless Component or Functional component
const myComponent = (props) => {
return (
<OtherComponent {...props} />
)
}
Job of this component is to just render pure jsx!
Stateful component or Class Component
`
class myComponent extends Component {
const { ...rest } = this.props
render() {
return <OtherComponent {...rest} />
}
}
`
Class components offer more features, and with more features comes more baggage. The primary reason to choose class components over functional components is that they can have state and lifecycle hooks with them such as componentWillMount(), componentDidUpdate()
So, to conclude if we want to render a component who is receiving props from its parent and no manipulation is to be done within their state then we can go for functional component
and about your second question
{...props} is identical to const { ...rest } = this.props
yes, it's identical!
Hope this helps !
I have multiple component with similar piece code in lifecycle methods and some similarity in state variables. Is there a way to unify them, by inheriting from one parent or something like that?
constructor(props) {
super(props);
this.state = {
//state properties similar in all components, getting from redux
//state properties specific for this component
}
// same code in many components
}
componentWillMount() {
// same code in many components
// code specific for this component
}
Can I use children methods and props in parent "wrapper" ? Can I change component state from parent ?
You can create Higher Order Component (HOC) for that, basically, you just write component with your same lifecycle method which is repeating, and then in render() function, call this.props.children function with any HOC internal state arguments you want, you can pass the whole state and a setState function as well, so you can change the HOC's state inside the underlying component.
For example:
class HOC extends React.Component {
constructor(props) {
super(props);
state = {
someState: 'foo',
};
}
componentWillMount() {
console.log('i mounted!')
}
render() {
return (
<div>
{this.props.children({ state: this.state, setState: this.setState })}
</div>
)
}
}
const SomeComponent = () =>
<HOC>
{({ state, setState }) => (
<div>
<span>someState value: </span>
<input
value={state.someState}
onChange={e => setState({ someState: e.target.value})}
/>
</div>
)}
</HOC>
You can also do really cool and interesting things with it, like connecting a slice of your redux state whenever you need it:
import { connect } from 'react-redux';
const ProfileState = connect(
state => ({ profile: state.profile }),
null,
)(({
profile,
children
}) => (
<div>
{children({ profile })}
</div>
));
const ProfilePage = () => (
<div>
Your name is:
<ProfileState>
{({ profile }) => (
<span>{profile.name}</span>
)}
</ProfileState>
</div>
);
Here is the full documentation on this technique.
You could create HOCs (Higher Order Components) in that case. It can look like this:
/*
A Higher Order Component is a function,
that takes a Component as Input and returns another Component.
Every Component that gets wrapped by this HOC
will receive `exampleProp`,`handleEvent`,
plus all other props that get passed in.
*/
function WithCommonLogic(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
example: ''
}
}
componentWillMount() {
...
// Same code in many components.
}
callback = () => {
/* Enhanced components can access this callback
via a prop called `handleEvent`
and thereby alter the state of their wrapper. */
this.setState({example: 'some val'})
}
render() {
return <WrappedComponent
exampleProp={this.state.example}
handleEvent={this.callback}
{...this.props}
/>
}
}
// You use it like this:
const EnhancedComponent1 = WithCommonLogic(SomeComponent);
const EnhancedComponent2 = WithCommonLogic(SomeOtherComponent);
Now all the shared logic goes into that HOC, which then wrap all your different components you want to share it with.
See the React Docs for further reading.
Having a container Component which hold state. it renders a number of stateless components.
I want to get access to all of their DOM nodes, so i can call the focus method on demand.
I am trying the ref approach as it is encouraged by the react documentation.
I'm getting the following error:
Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail.
What is the recommended way to get around this error?
preferably, without extra dom elements wrappers like exra divs.
Here is my Current Code:
Container Component - responsible for rendering the stateless components.
import React, { Component } from 'react';
import StatelessComponent from './components/stateless-component.jsx'
class Container extends Component {
constructor() {
super()
this.focusOnFirst = this.focusOnFirst.bind(this)
this.state = {
words: [
'hello',
'goodbye',
'see ya'
]
}
}
focusOnFirst() {
this.node1.focus()
}
render() {
return (
<div>
{
this.state.words.map((word,index)=>{
return <StatelessComponent
value={word}
ref={node => this[`node${index}`] = node}/>
})
}
<button onClick={this.focusOnFirst}>Focus on First Stateless Component</button>
</div>
)
}
}
Stateless Component - for sake of simplicity, just display a text inside a div.
import React from 'react';
export default function StatelessComponent(props) {
return <div>props.value</div>
}
Stateless (functional) components can't expose refs directly. However, if their internal components can use refs, you can pass a function from the parent (the grandparent) of the stateless component (the parent) as a ref via ref forwarding. Use the function as the ref target of the DOM element. Now the grandparent has direct access to the DOM element ref.
See Exposing DOM Refs to Parent Components in React's documentation.
const StatelessComponent = React.forwardRef((props, ref) => (
<div>
<input ref={ref} {...props} />
</div>
));
class Container extends React.Component {
itemRefs = []
componentDidMount() {
this.focusOnFirst();
}
focusOnFirst = () => this.itemRefs[0].focus()
inputRef = (ref) => this.itemRefs.push(ref)
render() {
return (
<div>
<StatelessComponent ref={this.inputRef} />
<StatelessComponent ref={this.inputRef} />
</div>
)
}
}
ReactDOM.render(
<Container />,
demo
)
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="demo"></div>
Try this, basically you pass a callback as ref to the stateless components that gets the input instance an adds it to an array owned by the container
class Container extends Component {
constructor() {
super()
this._inputs = [];
this.focusOnFirst = this.focusOnFirst.bind(this)
this.state = {
words: [
'hello',
'goodbye',
'see ya'
]
}
}
focusOnFirst() {
this._inputs[0].focus()
}
refCallback(ref) {
this._inputs.push(ref)
}
render() {
return (
<div>
{
this.state.words.map((word,index)=>{
return <StatelessComponent
value={word}
refCallback={this.refCallback}/>
})
}
<button onClick={this.focusOnFirst}>Focus on First Stateless Component</button>
</div>
)
}
}
And the stateless get modified a little too
function StatelessComponent({refCallback, value}) {
return <input ref={refCallback} value={value}/>
}
Here's a working plunker
I've got a parent component with react-router, setup like this :
constructor(props){
super(props);
this.state = {
diner: false
};
this.updateFromInvite = this.updateFromInvite.bind(this);
}
updateFromInvite(Souper) {
this.setState({diner: Souper});
}
I can't figure out how to setup the route to have both URL parameters and be able to pass a function to update the parent's state from the children component...
<Route path="/Invitation/:NomParam1?/:NomParam2?"
component = {() => (<Invitation updateApp = {this.updateFromInvite} />)} />
I think it's the closest I got...
From children's component :
class Invite extends Component {
constructor(props){
super(props);
this.state = {
diner: this.props.match.params.NomParam1 ,
JSONInfo: this.props.match.params.NomParam2
};
}
componentDidMount() {
const { diner } = this.state;
const { JSONInfo } = this.state;
const { updateApp } = this.props;
updateApp(diner);
}
render() {
return (
<div className="Invite">
<div className="col-centered">
<VidPlay/>
</div>
</div>
);
}
}
export default Invite;
The component property of the route takes a component Class, not an instance of the component. I believe you are looking to use the render property, which takes a rendered component. Your visual component shouldn't be concerned with the routing details, so you can pass that in in the Route configuration like so:
<Route path="/Invitation/:NomParam1?/:NomParam2?"
render={({match}) => (
<Invitation
updateApp={this.updateFromInvite}
diner={match.params.NomParam1}
JSONInfo={match.params.NomParam2}
/>
)}
/>
Then, in the component, don't utilize state, as that's not really what it is for:
class Invite extends Component {
componentDidMount() {
const { diner, JSONInfo, updateApp } = this.props;
// Not exactly sure what is going on here... how you
// will use JSONInfo, etc
updateApp(diner);
}
render() {
return (
<div className="Invite">
<div className="col-centered">
<VidPlay/>
</div>
</div>
);
}
}
Also, I'm not exactly sure what the parent component is doing, and why it is passing both the route params and the function down to the child, only to have the child call it back... but that is probably out of the scope of the question.
Enjoy!
If finally got it (thanks to that answer and the official documentation):
I needed to add props as parameter of my render and
use it with {...props} inside the children element!
<Route path="/Invitation/:NomParam1?/:NomParam2?"
render={ (props) =>
(<Invitation updateApp = {this.updateFromInvite} {...props} />)
}
/>
With that, I have access to BOTH :
my custom props
generic props (match, location and history)
Can I use an instance of a reactJS component to render a component.
Eg, Let's say my reactJS component is
class myComponent extends Component{
constructor(props){
super(props)
this.state = {
next:false
}
this.alertSomething = this.alertSomething.bind(this);
this.showNext = this.showNext.bind(this);
}
showNext(){
console.log('wow');
console.log(this.state, this, this.state.next);
this.setState({next:true});
}
alertSomething(){
alert('Alert Something')
console.log(this.state, this, this.state.next);
this.setState({next:true});
}
render(){
return(
<div className='column'>
</div>
)
}
}
export default myComponent
Now, inside my another component can I do;
let x = new displayContent.renderComponent();
render(
<x />
//or
<x.render />
)
// I tried both it didn't work, I thought there mush be some other way to achieve this, after all every component is just a javascript object.
Also at the same time, can I call function to make change in its state. Like.
x.someFunction();
where someFunctino is inside that react component, doing setState.
Is it possible? OR am I missing something?
Edit: I clearly understand that when you want to render a react component, you can always do, <component />.
This question is just out of curiosity, can this be done? if not, then why?, I mean how is that different from other javascript objects.
Well, you can use the React.createElement method to render a component:
React.createElement(Component, params)
but with JSX, this is the same:
<Component />
Refer to Multiple components in the React documentation.
This is not how you're supposed to use React. You don't have to handle object instantiations ; React do this for you. Use composition instead.
render() {
return (
<myComponent />
)
}
Also, if you want to set the state of a child component from a parent component, you should probably move the logic in the parent.
Probably you are looking for something like this.
import React, { Component } from "react";
import CamCapture from './CamCapture.js';
export default class ProctorVideoFeed extends Component{
constructor(props) {
super(props);
this.Camera = React.createElement(CamCapture);
}
//this.handleVideoClick = this.handleVideoClick.bind(this);
render(){
return(
<div>
<span>{this.Camera}</span>
<button onClick = {this.Camera.StopRecording}>Stop</button>
</div>
)
}
}
Here StopRecording is a function defined inside CamCapture class.