React get height of component - javascript

I need to know the height of a React Component inside another React component. I am aware that the height of an element can be reached by calling this.cmref.current.clientHeight. I'm looking for something like this:
child component:
const Comp = () =>{
return(
<div>some other stuff here</div>
)
}
export default Comp
parent component:
class App extends React.Component{
constructor(props){
super(props);
this.compref = React.createRef();
}
componentDidSomething(){
const height = this.compref.current.clientHeight;
//which will be undefined
}
render(){
return(
<div>
<Comp ref={this.compref} />
</div>
)
}
}
Is this possible? Thanks in advance.

You'll need to actually ref the div of the child component in order to get the element you want instead of the child component itself. To do this you could pass a function to the child that the child then passes to the div. Working example below:
const Comp = (props) =>{
return(
<div ref={props.onRef}>some other stuff here</div>
)
}
class App extends React.Component{
constructor(props){
super(props);
this.compref = React.createRef();
}
componentDidMount(){
const height = this.compref.current.clientHeight;
//which will be undefined --- No more!
console.log('height: ', height);
}
onCompRef = (ref) => {
this.compref.current = ref;
}
render(){
return(
<div>
<Comp onRef={this.onCompRef} />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root' style='width: 100%; height: 100%'>
</div>

const Comp = React.forwardRef((props, ref) => (
<div ref={ref}> some other stuff here </div>
));
class App extends React.Component{
constructor(props){
super(props);
this.compref = React.createRef();
}
componentDidMount(){
const height = this.compref.clientHeight;
console.log("hieght", height);
}
render(){
return(
<div>
<Comp ref={(el) => this.compref = el} />
</div>
)
}
}
ReactDOM.render(<App />, document.querySelector("#app"))
Could you try this way. Hope it helps. Please refer forward refs https://reactjs.org/docs/forwarding-refs.html

Related

Why would removing a component affects lifecycle of another component?

The following code example contains 2 components: Component1 logs prevState with componentDidUpdate method, Component2 is removed on a button click to show the use of componentWillUnmount.
const { useState } = React;
class Component1 extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'John',
};
}
componentDidUpdate(prevProps, prevState) {
console.log(prevState);
}
render() {
return (
<div>
<div>The name is {this.state.name}</div>
<label htmlFor="prevstate">Type here to see prevState in console: </label>
<input
id="prevstate"
onChange={(e) => {
this.setState({ name: e.target.value });
}}
/>
</div>
);
}
}
class Component2 extends React.Component {
componentDidMount() {
console.log('Mount');
}
componentWillUnmount() {
console.log('Unmount');
}
render() {
return (
<div>
<div>Click the button to remove the element</div>
</div>
);
}
}
const App = () => {
const [showComponent, setShowComponent] = useState(true);
return (
<div>
<Component1 />
{showComponent ? <Component2 /> : null}
<button
onClick={() => {
setShowComponent(false);
}}
>
Remove
</button>
</div>
);
};
// Render it
ReactDOM.render(<App />, document.getElementById('react'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Now I find that these 2 components interfere with each other: clicking the button also logs the prevState in the first component. What I thought would happen was that the componentDidUpdate method would only monitor the update of Component1, but seems it monitors the button which is outside of both components, why does this happen?
When the App is rendered, its children are rendered as well. You might try making Component1 a React.PureComponent or defining shouldComponentUpdate if you want to reduce the rerendering.

How to access properties in child from children props in react

The title is pretty straightforward, I need to access a property (a ref to be precise) on a child element that is passed through the children of my component, which means that I can't pass the ref in the parent afaik.
Here's a minimal example to highlight my issue:
import React from "react";
class Child extends React.Component {
myRef = React.createRef();
render() {
return <div ref={this.myRef}>child</div>;
}
}
const Parent = ({ children }) => {
const myChild = React.Children.toArray(children).find(
child => child.type === Child
);
// I want to access this
console.log(myChild.myRef);
// but it's undefined
return (
<div>
<h1>Parent</h1>
{children}
</div>
);
};
// I can't really change this component
export default function App() {
return (
<div className="App">
<Parent>
<Child />
</Parent>
</div>
);
}
I made a codesandbox highlighting my issue https://codesandbox.io/s/eloquent-wing-e0ejh?file=/src/App.js
Rather than declaring ref in <Child/>, you should declare ref in your <Parent/> and pass it to the child.
import React from "react";
class Child extends React.Component {
render() {
return <div ref={this.props.myRef}>child</div>;
}
}
const Parent = ({ children }) => {
const myRef = React.useRef(null);
// access it from here or do other thing
console.log(myRef);
return (
<div>
<h1>Parent</h1>
{ children(myRef) }
</div>
);
};
export default function App() {
return (
<div className="App">
<Parent>
{myRef => (
<Child myRef={myRef} />
)}
</Parent>
</div>
);
}

Lifting message up in REACT

I want to do a real time searching in React. How can I lift the message up from child to parent? Or how can I pass a parent handler to children through props to handle the event?
parent class:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activities: activities,
filteredActivities: activities,
};
this.handleSearchChange = this.handleSearchChange.bind(this);
}
filterActivity = searchText => {
return this.state.activities
.filter(activity => {
if(activity.content.toLowerCase().includes(
searchText.toLowerCase())
){
return true;
}
return false;
});
}
handleSearchChange = event => {
this.setState({
filteredActivities: this.filterActivity(event.target.value)
});
};
render() {
const filteredActivities = this.props.filteredActivities;
return(
<div className="notificationsFrame">
<div className="panel">
<Header name={this.props.name} />
<SearchBar onChange={this.handleSearchChange} />
<Content activities={this.state.filteredActivities} />
</div>
</div>
);
}
}
Child class:
class SearchBar extends React.Component {
onChangeHandler = event => {
console.log(event.target.value);
}
render() {
return (
<div className="search-bar" >
<input type="text" onChange={this.onChangeHandler} />
</div>
);
}
}
I want to pass the event.target.value to handleSearchChange. if I put the code of class Searchbar to class App, I can perform a real time searching very good. But I can't put them into two components. I want to make them into two components. Thanks a lot.
Should be as simple as this:-
Child class:
class SearchBar extends React.Component {
onChangeHandler = event => {
this.props.inputChanged(event.target.value);
}
render() {
return (
<div className="search-bar" >
<input type="text" onChange={this.onChangeHandler} />
</div>
);
}
}
Parent class:
handleSearchChange = inputValue => {
this.setState({
filteredActivities: this.filterActivity(inputValue)
});
};
JSX of parent class:
<SearchBar inputChanged={this.handleSearchChange} />
since you're already passing the function's reference as a prop you can access it using this.props.propName and call it.
class SearchBar extends React.Component {
/* onChangeHandler = event => {
console.log(event.target.value);
} */
render() {
const { onChange } = this.props;
return (
<div className="search-bar" >
<input type="text" onChange={onChange} />
</div>
);
}
}
You can try this, as you already took event as a parameter in parent class for handleSearchChange method

Using dynamic onClick with ref

Passing a dynamic property of onClick= do something by the use of ref gives me back: TypeError: _this.listReference is null listReference is defined in one of my components that i will show below.
In Component #1
class Component1 extends Component {
constructor(props){
super(props)
this.listReference= null;
}
//Returns
<div>
<SomeComponent list={(ref) => this.listReference= ref} />
<Component2 onMarkerClick = {(index) => {
this.listReference.scrollTop = 48 * index
}}/>
In Component #2
render() {
const {classes, driversStore, onMarkerCLick} = this.props
...
{driversStore.sortedSelectedOrders.map((order , index) => {
return (
<Component3
onClick={ () => onMarkerClick(index)} />
In Component #3
render() {
const { onClick } = this.props;
return (
<div
onClick={onClick}>
I expect upon click to trigger the scroll functionality (as Stated in Component #1).
Thanks in advance!
Check this example. Hope it can help you!
const Component2 = (props) =>(
<button onClick={props.onClick}>click me</button>
);
const SomeCompo = (props) =>(
<div>SomeComponent</div>
);
class Component1 extends React.Component{
listReference = React.createRef();
render(){
return(
<div>
<SomeCompo list={this.listReference}>reference</SomeCompo>
<Component2 onClick={this.handleClick} />
</div>
);
}
handleClick = () => {
if(this.listReference){
this.listReference={scrollTop:100};
}
console.log(this.listReference)
}
}
ReactDOM.render(<Component1/>,document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You should do the following in constructor,
this.listReference = React.createRef()

How can I append a tab everytime I click in react?

I want to output one tab everytime I click on the + button. I got it to output one. But now I am completely stumped. Here is My main component.
import React, { Component } from 'react';
import './App.css';
import InputTab from './components/tabs/InputTabs/InputTab';
import AddTab from './components/tabs/IncrementTabs/AddTab';
class App extends Component {
state = {
elementlist: ""
}
AddComponentHandler = event =>{
this.setState(
{elementlist: <InputTab/>}
);
}
render() {
return (
<div>
{this.state.elementlist}
<AddTab AddComp = {this.AddComponentHandler.bind(this)}
list = {this.state.elementlist}/>
</div>
);
}
}
export default App;
here is the component button which I want to click to append the input.
import React from 'react';
import './AddTab.css';
const AddTab = props => {
return(
<div onClick = {props.AddComp}
className = "addTab">
+
</div>
);
}
export default AddTab;
And for reference, here is my inputtab which I want to output everytime i click.
i hope i am being clear enough. Thank you in advance for help.
import React from 'react';
import './InputTab.css';
const InputTab = props => {
return(
<div className = "tabContainer">
<input className = "inputTabName"/>
<div className = "weightBox">
<input className = "inputTabWeight"/>%
</div>
</div>
);
}
export default InputTab;
The ideal way would be to store an array of data in state, and then map() over that array to render out multiple InputTabs:
class App extends React.Component {
state = {
elementlist: []
}
AddComponentHandler = event => {
this.setState( prevState => ({
elementlist: prevState.elementlist.concat([Date.now()])
}));
}
render() {
return (
<div>
{this.state.elementlist.map( el => <InputTab /> )}
<AddTab AddComp={this.AddComponentHandler} />
</div>
);
}
}
const AddTab = props => {
return(
<div onClick = {props.AddComp}
className = "addTab">
+
</div>
);
}
const InputTab = props => {
return(
<div className = "tabContainer">
<input className = "inputTabName"/>
<div className = "weightBox">
<input className = "inputTabWeight"/>%
</div>
</div>
);
}
ReactDOM.render(<App/>, 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 can just keep a simple integer counter of tabs in App state and increase it when click on AddTab button. Then you can just render one InputTab for each number from 0 to counter stored in state.
class App extends React.Component {
state = {
elementsCounter: 0
}
AddComponentHandler = event => {
this.setState( prevState => ({
elementsCounter: prevState.elementsCounter + 1
}));
}
render() {
return (
<div>
{[...Array(this.state.elementsCounter).keys()].map( index => <InputTab key={index} /> )}
<AddTab AddComp={this.AddComponentHandler} />
</div>
);
}
}
const AddTab = props => {
return(
<div onClick = {props.AddComp}
className = "addTab">
+
</div>
);
}
const InputTab = props => {
return(
<div className = "tabContainer">
<input className = "inputTabName"/>
<div className = "weightBox">
<input className = "inputTabWeight"/>%
</div>
</div>
);
}
ReactDOM.render(<App/>, 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>

Categories

Resources