I use TypeScript and class component way and I need to save reference on my element. I am going to get the state of target element when 'resize' event will happen.
Accordingly How to use refs in React with Typescript I tried this:
export default class Carousel extends Component<CarouselProps, CarouselState> {
private ref: React.LegacyRef<HTMLDivElement>;
constructor(...) {
this.ref = React.createRef();
window.addEventListener('resize', (e) => this.resizeCarouselElements());
}
componentDidUpdate(...){...}
componentDidMount(...){...}
resizeCarouselElements = () => {
<...get current state of target element here ...>
}
render() {
return (
<div className='name' ref={this.ref}>
})
}
But after resizing the window I have got null:
How can I save reference on my div className='name' and use it after user's window resizing? Which way is the best practice? Store the ref in state, const or somewhere else?
Related
On button click, I want scroll to component
class Host extends Component {
static contextType = HostContext;
constructor(props) {
super(props);
this.dateSelectorRef = React.createRef();
}
setStartDate () {
this.dateSelectorRef.current.setStartDate()
console.log('Not working', this.dateSelectorRef.current.scrollIntoView)
}
render() {
........
<DirectButton clickhandler={() => this.setStartDate()} />
........
........
<DateSelector
ref={this.dateSelectorRef}
onStartDateChange={this.onStartDateChange}
onEndDateChange={this.onEndDateChange}
/>
}
Above code, while I click on DirectButton event handler setStartDate call
but this.dateSelectorRef.current have no object scrollIntoView (undefined)
Any Idea what am I missing.
The DateSelector component does not implement this method, this is a DOM element method. You'll need to find a way to access the ref of the DOM element rendered by this component.
setStartDate = () => {
this.dateSelectorRef.current.setStartDate()
console.log('Not working', this.dateSelectorRef.current.scrollIntoView) }
This code will fix your issue
I have a couple of React components that are all based of the same base class, these component have a couple of properties which I would like to read before I render the component. This has to do with some conditions that are used somewhere else.
Currently I am calling a method, with something like this in my Render function.
public getWidget(): JSX.Element {
let widget = null;
switch (widgetType) {
case 'widget1': {
widgetComponent = new Widget1(props); // private variable in my class
widget = (<Widget1 { ...props } ref = { some-ref });
}
case 'widget2': {
widgetComponent = new Widget2(props); // private variable in my class
widget = (<Widget2 { ...props } ref = { some-ref });
}
}
return widget;
}
This way I can ask the widget some stuff about it's default values and render the widget variable in my Render function, like this:
render() {
const widget = this.getWidget();
const somethingIWantToKnow = this.widgetComponent.someProperty;
return ({ widget });
}
From what I understand, the reference I set for my React Component is only available after I render? Otherwise I could just use that.
I also tried calling this.widgetComponent.render() in my own Render method, but this does not set up the component correctly (probably because of missing componentWillMount and componentDidMount calls.
I just can't believe this is the way to go, is there a way to render from this.widgetComponent in my Render method, or is there a way to get properties from the class behind a JSX.Element?
NULL checks and other stuff is all removed from these code snippets :)
Give your widget a ref,
widget = (<Widget1 { ...props } ref = { widget1 } />);
Then you can access your instantiated component in componentDidMount and use the ref to access the property,
componentDidMount(){
const somethingIWantToKnow = this.widget1.current.someProperty
}
For the rare times when you need a reference to another JSX element in React, you can use the ref prop, like this:
class Widget extends React.PureComponent {
example() {
// do something
}
render() {
...
<Widget ref={r => this.mywidget = r}/>
<OtherWidget onClick={e => this.mywidget.example()}/>
Here, the Widget instance is stored in this.mywidget for later use, and the example() function can be called on it.
In Material UI, you can wrap components around a withTheme() call to make the theme accessible in their props:
export default withTheme()(Widget);
However if this is done, the ref receives an instance of WithTheme rather than Widget. This means the example() function is no longer accessible.
Is there some way to use ref with a component wrapped by withTheme() so that the underlying object can still be accessed, in the same manner as if withTheme() had not been used?
Here is an example demonstrating the issue. Lines 27 and 28 can be commented/uncommented to see that things only fail when the withTheme() call is added.
In order to get the ref of the component which is wrapped with withStyles, you can create a wrapper around Widget, and use that with withStyles like
const WithRefWidget = ({ innerRef, ...rest }) => {
console.log(innerRef);
return <Widget ref={innerRef} {...rest} />;
};
const MyWidget = withTheme()(WithRefWidget);
class Demo extends React.PureComponent {
constructor(props) {
super(props);
this.mywidget = null;
}
render() {
return (
<React.Fragment>
<MyWidget
innerRef={r => {
console.log(r);
this.mywidget = r;
}}
/>
<Button
onClick={e => {
e.preventDefault();
console.log(this.mywidget);
}}
variant="raised"
>
Click
</Button>
</React.Fragment>
);
}
}
Have a look at this answer to see an other alternative approach
losing functions when using recompose component as ref
This is a shorter alternative based on Shubham Khatri's answer. That answer works when you can't alter the inner component, this example is a bit shorter when you can modify the inner component.
Essentially ref doesn't get passed through withTheme() so you have to use a prop with a different name, and implement ref functionality on it yourself:
class Widget extends React.PureComponent {
constructor(props) {
props.ref2(this); // duplicate 'ref' functionality for the 'ref2' prop
...
const MyWidget = withTheme()(Widget);
...
<MyWidget
ref2={r => {
console.log(r);
this.mywidget = r;
}}
/>
I am trying to get the height of an image when it has loaded and send it back to the parent component, but it is causing infinite rerendering.
This is a prototype of my code:
import MyImage from './images/myImage.jpg';
class Image extends React.Component {
constructor(props) {
super(props);
this.state = {
height: 0
}
}
getHeight = (e) => {
const height = e.target.getBoundingClientRect().height;
this.setState({
height: height
});
this.props.setUnitHeight(height);
}
render() {
const image = this.props.image;
return (
<img src={image.name} onLoad={(e)=>{this.getHeight(e)}} />;
);
}
}
class App extends Component {
constructor(props) {
super(props);
const initUnit = 78.4;
this.state = {
unit: initUnit
}
}
setUnitHeight = (height) => {
this.setState({
unit: height
});
}
render() {
return (
<div>
<Image image={MyImage} setUnitHeight={this.setUnitHeight} />
</div>
);
}
}
I have tried sending unit as a prop and then checking in shouldComponentUpdate whether it should be rerender or not, but that did nothing.
The issue you are having is that React by default re-renders the component every time you call this.setState. In your case what this is happening:
You load your Image component
It loads the <img> tag and fires the onLoad function
The onLoad function calls this.setState
Repeat these steps forever
You should take a look at the React's lifecycle components methods (https://reactjs.org/docs/react-component.html#the-component-lifecycle) to understand this better.
My suggestion is: do not keep the image height in the state, unless you really need it. If you really need to maintain it in the state for some reason you can use the lifecycle method shouldComponentUpdate (https://reactjs.org/docs/react-component.html#shouldcomponentupdate`) to prevent it from rendering.
Your code seems redundant, setState({}) isn't necessary in <Image> class. If you are using the same props throughout the app, then you should be setting it at one place and be using the same prop all over. For example -
getHeight = (e) => {
const height = e.target.getBoundingClientRect().height;
//setState not needed here
this.props.setUnitHeight(height);
}
That should do it.
P.S: Do check if your this references aren't going out of scope.
I am working on implementing some canvas rendering by using Components. I have the following Component which attempts to layout the canvas to the page, and have subcomponents in the Stage component draw to its context.
I need a reference to the canvas elements that my Renderer component is rendering. So I ask for the ref in the JSX, and set it on my component when I get it. I'm also using React context, so that all the children components can access this canvas reference. The problem I'm having is that it seems getChildContext is called before the canvas ref is assigned and so all the children component access an undefined canvas and no drawing is done.
export default class Renderer extends React.Component {
static childContextTypes = {
canvas: PropTypes.object
};
getChildContext() {
console.log("get child context")
return { canvas: this.canvas };
}
render() {
return (
<div className="CanvasHolder" key={0}>
<canvas className="MainCanvas" ref= {
canvas => {
this.canvas = canvas
console.log("got ref");
}
} />
<Stage />
</div>
)
}
}
You can access the ref only after in componentDidMount (after it is rendered). But the parent component is considered rendered only when all of its children are rendered. So children can't access parent's ref before it is rendered. Instead of returning canvas ref directly, try to pass custom function getCanvas() instead:
getCanvas = () => {
return this.canvas
}
And then in your children component call this function to retrieve canvas ref.