getting React elements offsetTop - javascript

in react when I get the offsetTop of the element in the componentDidMount is different than the offsetTop value of the same element when called in componentDidUpdate(). Why is that? I thought componentDidMount is called after render so the DOM elements are placed in the page so the offsetTop value should be correct.
class Index extends React.Component {
...
render() {
return (
<div className="site-wrapper">
<TopHeader />
<IntroSection />
</div>
);
}
}
class TopHeader extends React.Component {
...
componentDidMount() {
var rect = ReactDOM.findDOMNode(this).offsetTop;
console.log(rect);
}
}
class IntroSection extends React.Component {
...
componentDidMount() {
var rect = ReactDOM.findDOMNode(this).offsetTop;
console.log(rect);
}
}

componentDidMount is called once and only one . However, componentDidUpdate is called for each update that component received , even through state or props.
Then , we recommend to persist the offsetTop in the component state for both lifecycle methods .

Related

Can't get Child Component ref object from their parent React JS

I want to reference a <div> and a <span> component from the child to the parent.
So i code something like:
class Parent extends Component {
childRef = React.createRef()
componentDidMount(){
const childRef1 = this.childRef.current.innerRef1
const childRef2 = this.childRef.current.innerRef2
//... compute with the references childRef1 and childRef2
}
render(){
return(
<ChildComponent ref={this.childRef} />
)
}
}
Inside the child i got something like:
class ChildComponent extends Component {
innerRef1 = React.createRef()
innerRef2 = React.createRef()
render(){
return(
<div ref={this.innerRef1}>
<span ref={this.innerRef2}></span>
</div>
)
}
}
I want to get the properties of those tags. Things like getBoundingClientRect(), scrollTop,etc; but from the Parent component because i can't compute it from the ChildComponent componentDidMount because those component aren't rendered yet.
That's is my current result from browser console:
As you can see, the current object shows me a null value, but inside you can see all the properties that i want to get.
As you want to get the properties of those tags like getBoundingClientRect(). I have provided the example where I called getBoundingClientRect() using ref and also I set a string into innerText of span. Please check it.
Parent Component Example
import React from "react";
import ChildComponentExample from "./ChildComponentExample";
export default class ParentComponentExample extends React.Component {
childRef = React.createRef();
componentDidMount() {
const childRef1 = this.childRef.current.innerRef1;
const childRef2 = this.childRef.current.innerRef2;
console.log(childRef2, 'childRef2');
childRef2.current.innerText = 'This is SPAN';
const rect = childRef1.current.getBoundingClientRect();
console.log(rect, 'rect');
}
render() {
return (
<ChildComponentExample ref={this.childRef}/>
)
}
}
Child Component Example
import React from "react";
export default class ChildComponentExample extends React.Component {
innerRef1 = React.createRef();
innerRef2 = React.createRef();
render() {
return (
<div ref={this.innerRef1}>
<span ref={this.innerRef2}/>
</div>
)
}
}

React how to access props from constructor and then make a component based on the props/state

I am not really sure how to properly ask this question but I will explain what I'm trying to do here:
So I have this parent Component which creates a Component like so:
<CurrentTemperature cityName={this.state.cityName}></CurrentTemperature>
The CurrentTemperature Component looks like this:
import React, { Component } from "react";
import "../App.css";
export default class CurrentTemperature extends Component {
constructor(props) {
super(props);
this.state = {
temperature: 0,
cityName: this.props.cityName,
};
}
componentDidMount() {
//fetch the temperature from api here
}
render() {
return (
<div>
<div className="city-temperature">
{this.state.cityName} {this.state.temperature}
</div>
</div>
);
}
}
All I'm trying to do is read the city name from the parent, then fetch the current temperature from my API, and then display both of those in the Component. But if I try to console.log(this.props.cityName) from anywhere other than from inside the city-temperature div, I always get an empty string. What is going on here?
cityName is the state of the parent component. I guess the parent component would get the "cityName" asynchronously. right? If this is the case, You have to put the temperature in the parent component as its state. And you have to insert the API call in the parent component. CurrentTemperature component will behave like a pure function component.
const CurrentTemperature = ({temperature, cityName}) => {
return (
<div className="city-temperature">
{cityName} {temperature}
</div>
);
}
I guess this is not only the solution but also the best DX.
You can remove this in your constructor, and then use this.state.cityName
constructor(props) {
super(props);
this.state = {
temperature: 0,
cityName: props.cityName,
};
}

React - How do you get the top position of a styled component?

Something that used to be simple is quite complicated when you don't know the React way.
I'm trying to create a component to act like a sticky header or footer when it reaches the top of the page.
At the moment I'm quite content in adding the below:
componentDidMount() {
window.addEventListener('scroll', this.onScroll, false);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.onScroll, false);
}
However I've hit a wall to how I get the scroll top position of a styled component. So lets say I had a styled component called Container and that outputted a form I usually just add a data attribute and do something like the below:
const container = document.getElementbyTag("data-sticky-container")
const position = container.offsetTop
How would I go about doing this in React?
Update
Now using ref. I've fallen into a problem where current isn't defined:
constructor(props) {
super(props);
this.optionBox = React.createRef();
}
componentDidMount() {
window.addEventListener('scroll', this.onScroll, false);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.onScroll, false);
}
onScroll() {
console.log(this.optionBox.current.offsetTop);
}
In react you would use a reference for your component: https://reactjs.org/docs/refs-and-the-dom.html
You would have something like this:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
getOffset = () => {
console.log(this.myRef.current.offsetTop);
}
render() {
return <Container ref={this.myRef} onClick={this.getOffset} />;
}
}
And now you can access the container offsetTop by this.myRef.current.offsetTop inside of your onScroll function like in getOffset
Another option is to use an innerRef. This can be better because it will give you a reference to the DOM node and not just a wrapper. See this answer.
You would then have more direct access to properties like scrollTop.
Unlike ref, innerRef uses a callback:
In your case it would look like:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef;
}
getOffset = () => {
console.log(this.myRef.offsetTop);
}
render() {
return <Container
innerRef={element => this.textInput = element}
onClick={this.getOffset} />;
}
}
You can add a ref
to the component for which u want an offsetTop. This ref will have all the computed css values of that component.

Accesing the state of child component

I have a parent component which contains a function, which when called needs to acces the childrenĀ“s component state. I dont want to move the whole state to the parent component because i want the children component to be independent. What is the cleanest and most recommended way to achieve this?
class ParentComponent extends Component {
render() {
return (
<div>
<ChildComponent/>
<SaveButton onClick={this.saveFunction}/>
</div>
)
}
saveFunction = () => {
//Here i need to acces the child Component state
}
}
My solution so far was that everytime something changed in child component i called a function which was passed from the parent Component. Like this:
class ChildrenComponent extends Component {
state = {
name: "David",
age: 19
}
render() {
return (
//inputs with the inputChange function
)
}
inputChange = (e) => {
//Update the state
//Then pass the state to the parent
this.props.passStateToParent(this.state)
}
}
I would recommend to look up some of the React patterns - especially Render Props, as it allows to expose the state and wanted methods of a component - what you want in this situation.
Best of luck!
You can make a function in parent component and pass it down to child component as prop. This function could return to parent component the state of your child component. See more here: https://reactjs.org/docs/faq-functions.html
You cannot directly access the state of the child component,this can be done by passing the state to methods of parent component which are passed as props to child component,the following example demonstrate how to do it .
class ParentComponent extends React.Component {
somefunc() {
//do your action
}
render() {
<ChildComponent parentfunc={this.somefunc}/>
}
}
class ChildComponent extends React.Component {
constructor(props){
super(props)
this.state = {somedate:'value'}
this.func = this.func.bind(this)
}
func() {
this.props.parentfunc(this.state)
}
render() {
<button onClick={this.func}>text</button>
}
}

How to avoid the setState() / render() endless loop when passing data from child to parent?

I am trying to save child-data in the state of the parent, but end up with the endless loop because setState() calls render().
Error message: Maximum update depth exceeded.This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
Relatively new to React, so I can't seem to word the question when googling solutions. I know why the error is occurring, I just don't know how to get around the issue. Is there a specific method I can use that prevents re-rendering?
Here is the parent:
export class ToDoList extends React.Component {
constructor(props){
super(props);
this.state = {
data: null
}
}
myCallback = (dataFromChild) => {
this.setState({data: dataFromChild.toUpperCase()})
}
render() {
return (
<div>
<ToDoItem callbackFromParent={this.myCallback}/>
</div>
);
}
}
The child:
class ToDoItem extends React.Component {
constructor(props){
super(props);
this.state = {
listInfo: 'Doggos'
}
}
render(){
return(
<h1>{this.props.callbackFromParent(this.state.listInfo)}</h1>
);
}
}
Your code is doing exactly that, an endless loop. When your ToDoItem component renders, it calls callbackFromParent which updates the state of ToDoList, causing ToDoList to re-render, subsequently re-rendering the ToDoItem. Since ToDoItem re-renders, it calls callbackFromParent again and so on...
I'd like to ask why you are trying to render the non-value-returning function of callbackFromParent. It doesn't return anything, so it doesn't make sense why you'd want to render it inside of your <h1> tags.
There is a small problem with the code you shared, that you are calling a function from the render() rather than binding it to some event which is making it go into infinite loop...
Is this what you are trying to achieve?
class ToDoList extends React.Component {
toUpper = (dataFromChild) => {
return dataFromChild.toUpperCase();
}
render() {
return (
<div>
<ToDoItem toUpper={this.toUpper}/>
</div>
);
}
}
class ToDoItem extends React.Component {
constructor(props){
super(props);
this.state = {
listInfo: 'Doggos'
}
}
render(){
return(
<h1>{this.props.toUpper(this.state.listInfo)}</h1>
);
}
}
ReactDOM.render(<ToDoList />, 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>
You are getting an endless loop because of the following reasons:
1) You are calling the parent callback on each render.
2) You are saving the uppercased value in the parent state.
When the parent state gets updated, the child gets re-rendered, meaning that it will call the callback again, which will cause to re-render, which calls the callback again etc...
An alternative solution would be to pass the util function down to the child which can then call it once when it re-renders. Since no state in the parent is being updated, the child will not be re-rendered.
If you're trying to save data on parent but want to display it in child, try this:
Here is the parent:
export class ToDoList extends React.Component {
constructor(props){
super(props);
this.state = {
data: null
}
}
myCallback = (dataFromChild) => {
this.setState({data: dataFromChild})
}
render() {
return (
<div>
<ToDoItem callbackFromParent={this.myCallback} data={this.state.data}/>
</div>
);
}
}
The child:
class ToDoItem extends React.Component {
constructor(props){
super(props);
this.state = {
times: 0
}
// Bind Explained below
this.iBeClicked = this.iBeClicked.bind(this);
}
iBeClicked(){
this.setState({times: ++this.props.data});
this.props.callbackFromParent(this.props.data++);
}
render(){
return(
<div className="wrap">
<h1 onClick="iBeClicked">{this.props.data !== null ? this.props.data: 'Nothing' }</h1>
</div>
);
}
}
You use this.method.bind(this) in order to bind Component's this to React callback's execution inside render.

Categories