React props.children width - reactjs

Is there anyway of getting the width of React component children. I have wrapper component called for lack of name Container and I add children of div type from Component1 to it. See below example.
I'm wondering if there is a way to get the width of each div child in Container when it mounts.
UPDATED NOTE:
The reason I'm trying to get the containers children widths is so I can dynamical set the containers width based on the total number of children. By setting the containers width to the number of children's width then I can allow for some horizontal scrolling effects I want to do.
Component 1
export default class Component1 extends Component{
render(){
return(
<Container>
<div className="large-box"/>
<div className="large-box-dark"/>
</Container>
)
}
}
Now my Container component.
export default class Container extends Component{
componentDidMount(){
this.props.children.forEach(( el ) => {
// get each child's width
console.log("el =", el);
});
}
render() {
return (
<div className="scroller" ref={(scroller) => { this.scroller = scroller }}>
{this.props.children}
</div>
)
}
}

You can use refs. Although you should avoid touching the DOM until and unless there is no other way. But here we go.
Give your child components a ref which is escape hatch provided by React to access the DOM(with a warning to use other methods before coming to this).
Child Component
class ChildComp extends Component {
getWidth = () => {
//Access the node here and get the width
return this.childNode.offsetWidth(); //or clientWidth();
}
render() {
return (
<div ref={(r) => {this.childNode = r}}>
</div>
);
}
}
Parent Component:
class ParentComp extends Component {
componentDidMount() {
//Access the child component function from here
this.childComponent.getWidth();
}
render() {
return (
<ChildComp
ref={(r) => {this.childComponent = r}} />
);
}
}
But do remember use the above method when there is no way of getting the thing done declaratively.

I would say that technically it doesn't seem possible. The JSX
<div className="large-box"/>
does not refer to DOM element, (which has a width once it's been rendered) but to a React element, which is an in-memory object describing how to make an DOM element. Since the React element isn't rendered or even connected to the actual DOM in the browser, it can't know the width.
Remember that React can be rendered on the server -- there's no way the server can know what the browser on different computer is going to display.
I'd also echo what Pedro Nascimento noted -- this solution is probably best solved some other way, but without context, it's difficult to help.

then try to get the ref of "DivColorOpacy"! and put whatever you want on parent and cutom your behavior. That's silly but that do the job.
with this css
.DivColorOpacy{
height: max-content;
width: max-content;
position: relative;
}
import { Component } from "react";
import * as React from "react";
interface DivColorOpacyProps {
backgroundColor: string,
opacity: number,
}
export class DivColorOpacy extends Component<DivColorOpacyProps, any>{
componentDidMount(){
}
render() {
const { backgroundColor, opacity } = this.props;
return <div className="DivColorOpacy">
{this.props.children}
<div style={{
position: "absolute",
zIndex: -1,
backgroundColor,
opacity,
width: "100%",
height: "100%",
top:0
}} />
</div>
}
}

Related

Reactjs state changes do not propagate to dynamically-created children components

I'm learning react and while working on a bigger project I created this mockup to show the issue I'm having.
The parent component maintains a value in state which it passes to children via props. I want this value to propagate to children and update there when it is changed in the parent state. This works in the first version of this code:
import React from "react"
import Child from './Child'
export default class Parent extends React.Component {
constructor() {
super();
this.state = {
single_val: false,
}
}
render() {
return(
<div className="Parent" style={{border: "solid orange 1px", padding: "15px"}}>
<p>Parent val: {this.state.single_val.toString()}</p>
<Child parent_val={this.state.single_val}/>
<Child parent_val={this.state.single_val}/>
<Child parent_val={this.state.single_val}/>
<div className="switch"
style={{height: "50px", width: "50px", backgroundColor: "lightPink"}}
onClick={(e)=>{this.setState({single_val: true})}}
>
</div>
</div>
)
}
}
However, in the final version of the project, I need to create the children dynamically. I do it like this:
import React from "react"
import Child from './Child'
export default class Parent extends React.Component {
constructor() {
super();
this.state = {
single_val: false,
children_divs: [],
}
this.setUp = this.setUp.bind(this);
}
componentDidMount() {
this.setUp();
}
setUp() {
var baseArray = [...Array(3)];
var children = baseArray.map((elem)=>{
return (<Child parent_val={this.state.single_val} />)
});
this.setState({children_divs: children});
}
render() {
return(
<div className="Parent" style={{border: "solid orange 1px", padding: "15px"}}>
<p>Parent val: {this.state.single_val.toString()}</p>
{this.state.children_divs}
<div className="switch"
style={{height: "50px", width: "50px", backgroundColor: "lightPink"}}
onClick={(e)=>{this.setState({single_val: true})}}
>
</div>
</div>
)
}
}
...and the value no longer propagates to children when I press the button and change the parent's state: results screenshots.
How to keep the dynamic creation of child divs and still have the parent value propagate? I sense the issue might me because the value and children divs array are both maintained in the parent state but I'm not sure how to fix it. Hours of searching and looking at examples suggest I should recreate children divs from scratch - run the setUp again - but it seems like an overkill for one state value that I thought should propagate anyway.
Child component code for reference:
import React from "react"
export default function Child(props) {
return (
<div className="Child">
<p>Child val: {props.parent_val.toString()}</p>
</div>
)
}
P.S. I even experimented with adding componentDidUpdate() to children to try and receive props again, but it never triggered.
Ok, so the problem here is your children_divs are created once and value of single_val is added/sent to them at that time (when you have created them in setUp function.
Solution is simple, have your children created in render function, as render is called each time your state changes. This also removes your children_divs from state as its only used to render and serve no other purpose.
import React from "react"
import Child from './Child'
export default class Parent extends React.Component {
constructor() {
super();
this.state = {
single_val: false,
}
}
render() {
var baseArray = [...Array(3)];
var children_divs= baseArray.map((elem)=>{
return (<Child parent_val={this.state.single_val} />)
});
return(
<div className="Parent" style={{border: "solid orange 1px", padding: "15px"}}>
<p>Parent val: {this.state.single_val.toString()}</p>
{children_divs}
<div className="switch"
style={{height: "50px", width: "50px", backgroundColor: "lightPink"}}
onClick={(e)=>{this.setState({single_val: true})}}
>
</div>
</div>
)
}
}

How to get rendered JSX in React?

Imagine a Container component that renders a div with the specified height, e.g.:
<Container height="80">
Hello World
</Container>
and MyHeader component that renders a Container with a certain height, e.g.:
function MyHeader() {
return (
<Container height="100">
Header content goes here
</Container>
);
}
Now, I'd like to implement a Fixed component that looks like this:
<Fixed>
<Fixed.Item>
<MyHeader />
</Fixed.Item>
<Fixed.Content>
Some content goes here
</Fixed.Content>
</Fixed>
When rendering Fixed.Content I'd like to automatically set its offset to 100px (since MyHeader is 100px high).
Is there a way for the Fixed component to get MyHeader's height so it could pass it to Fixed.Content?
Is there a better way to automate this?
Note: Using useEffect (or componentDidMount) is not an option because I'd like it to work in server rendered environments.
Generally you want data flowing down from parents to children. If that is not an option for you, you can use Contexts: https://reactjs.org/docs/context.html. Especially check out https://reactjs.org/docs/context.html#updating-context-from-a-nested-component. In your case, MyHeader could be a Consumer of your context and update it with its height, and Fixed.Content would also be a consumer that uses the value for its offset. But in general, I'd say what you're trying to do is a bit unnatural.
You can use refs for that.
To solve your specific problem, first turn your <Container> component into a class component to be able to set a ref to it.
Then use React.forwardRef to forward the ref from the MyHeader component to the <Container> component:
const MyHeader = React.forwardRef((props, ref) => {
return (
<Container ref={ref} height={100}>
Header content goes here
</Container>
);
});
Finally, create a ref hook in your component that renders the <Fixed> component and pass the ref to the <MyHeader> component. You can then also use the ref to set the height of the <Fixed.Content> component (or whatever you want to set), as follows:
function App () {
const headerRef = React.useRef(null)
return (
<Fixed>
<Fixed.Item>
<MyHeader ref={headerRef} />
</Fixed.Item>
<Fixed.Content height={headerRef.current && headerRef.current.props.height}>
Some content goes here
</Fixed.Content>
</Fixed>
)
}
This seems to only render the <App> component once, so it should also work for server-side rendering. See the following code snippet as an example: https://codesandbox.io/s/get-height-from-header-gvvlo
You can use React Test Renderer which renders React Node to an inspectable Object tree.
import TestRenderer from 'react-test-renderer';
const height = TestRenderer.create(<MyHeader />).toTree().rendered.props.height
This way, you can get its height without second render.
function Container(props) {
return (
<div style={{height:props.height}}>
your content
</div>
);
}
you need to pass height to Container as props which can varied dynamically.
function MyHeader() {
return (
<Container height="100px">
Header content goes here
</Container>
);
}
for fixed also you need to pass it as props by having the height in a variable,e.g.:
function TopBar() {
let height = '100px'
return (
<>
<MyHeader height={height} />
<FixedComponent height={height} />
</>
);
}
I think you need to pass height prop from MyHeader to Fixed. And then Fixed to Fixed.Content. For example you can do similar to the following:
MyHeader.jsx
class MyHeader extends React.Component {
constructor(props) {
super(props)
this.state = {
height: 100
}
this.props.setHeaderOffset(this.state.height)
}
render() {
return (
<Container height={this.state.height}>
</Container>
)
}
}
Fixed.jsx
class Fixed extends React.Component {
constructor(props) {
super(props)
this.state = {
headerOffset: 0
}
this.setHeaderOffset = this.setHeaderOffset.bind(this)
}
setHeaderOffset(headerOffset) {
this.setState({
headerOffset
})
}
render() {
return (
<>
<Item>
<MyHeader setHeaderOffset={this.setHeaderOffset} />
</Item>
<Content headerOffset={this.state.headerOffset}>
Some content goes here
</Content>
</>
)
}
}

In React JS, how do I tell a parent component that something has happened in the child?

I have a React JS app with a simple hierarchy: ContainingBox wraps two InfoBox components. in this example, I simply want to tell the ContainingBox component 1) that something has been clicked, and 2) which InfoBox (by label name) has been clicked?
Here is some basic code that works in my browser to get this question up & running. All it does it console.log when you click onto one of the InfoBox elements on the page.
Essentially, what I am trying to achieve is that I want the ContainingBox to change state (specifically, border color as rendered) when one of the child InfoBox elements is clicked.
I'm not sure what the right direction here is.
I built this app with React 16.10.2, but I would be happy to read answers pointing me towards the latest 'React way' of thinking.
import React from 'react';
import styled from 'styled-components'
import './App.css';
const StyledInfoBox = styled.div`
width: 100px;
border: solid 1px green;
padding: 10px;
cursor: pointer;
`
class InfoBox extends React.Component {
constructor({blurb}) {
super()
this.state = {
label: (blurb ? blurb.label : ""),
}
this.selectBox = this.selectBox.bind(this);
}
selectBox(e) {
e.preventDefault()
console.log("selectBox")
// how do I tell the ContainingBox component 1) that something has been clicked,
// and 2) which InfoBox (by label name) has been clicked?
}
render() {
const {label} = this.state
return (
<StyledInfoBox onClick={this.selectBox} >
{label}
</StyledInfoBox>
)
}
}
class ContainingBox extends React.Component {
render() {
return (
<div>
<InfoBox key={1} blurb={{label: "Aenean malesuada lorem"}} />
<InfoBox key={2} blurb={{label: "Lorem Ipsum dor ameet"}} />
</div>
)
}
}
function App() {
return (
<div className="App">
<ContainingBox />
</div>
)
}
export default App;
You pass a callback from the parent component to child component via the props.
class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
changeNameTo = (newName) => this.setState({name: newName})
render() {
return (
<div>
<h1>{this.state.name}</h1>
<p>
<Child callbackExample={this.changeNameTo} />
</p>
</div>
);
}
}
Then you have your Child component.
class Child extends Component {
render() {
return(
<div>
<button onClick={() => this.props.callbackExample("Doggos")}>
Click me
</button>
</div>)
}
}
When you click the button, the callback is invoked setting the state of the parent, which is then reflected when the parent re-renders.

Locate a component in React project

I wrote a Logout button component in my React app for which I wish to locate at the top right corner of the screen.
render() {
<LogoutButtonComponent height: , backgroudColor: />
}
It wouldn't let me assign any values for height and etc.
This is the Logout component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class LogOutButton extends Component {
static contextTypes = {
store: PropTypes.object.isRequired,
};
handleClick = () => {
this.props.onLogout();
};
render() {
return <button type="button" onClick={this.handleClick}>Logout</button>;
}
}
Should I locate it by < col /> ?
To add inline styles, you should defined a style object as prop, and pass it values, like doniyor2109 mentioned. There are a few caveats to using this, however.
style={{ height: 100, height: '100px', height: '100%', minHeight: '100px'}}.
Not every value should be passed as integer, some need to be passed as a string
Not every css attribute gets passed as you would expect them to, the css min-height actually gets passed as minHeight, so replace all hyphens with lower camel case style
Inline styles get insanely difficult to manage. I suggest you at the very least create an object outside the component, and pass it in like:
const DivStyle = { minHeight: '100px' }
and then:
<LogoutButtonComponent style={DivStyle} />
You can prefix that DivStyle with an export if you want to import {DivStyle} from './somefile' in other places
I suggest you check out a library like styled-components as it makes styling much easier!
I suggest you check out this article which outlines your options
You don't really add styles to your component like that. It's better to add those styles in the source for the actual component. So how exactly do you want it displayed? I will provide a template kind of and you can change it to what you want.
Go to your source for your Logout Button Component. In the return of your render method try adding a div call it container. Then add styling in a css file to that div or if you are using react-bootstrap or reactstrap or #material/ui/core you can adjust the style according to their documentation.
You can add your css for the className .container to make it appear the way you would like.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class LogOutButton extends Component {
static contextTypes = {
store: PropTypes.object.isRequired,
};
handleClick = () => {
this.props.onLogout();
};
render() {
return (
<div className="container">
{* notice the className here *}
<button type="button" onClick={this.handleClick}>Logout</button>
</div>
)
}
}
Hope this helps.

size element based on another element's dimensions in reactjs

Say I have an element like this:
export default class DemoAxis extends Component {
componentDidMount() {
const el = findDOMNode(this);
this.dimensions = getDimensionsFromNode(el);
}
render() {
const style = {
parent: {border: "1px solid #ccc", margin: "2%", maxWidth: "40%", padding: 10}
};
return (
<div ref="line">
<svg style={style} width={this.dimensions.width} height={this.dimensions.height}
I want to size the svg element based on the dimensions of the line element.
The element's dimensions will only be available in componentDidMount which is called after render.
I know I can call setState but that will cause a re-render and that might give me UI issues.
What is the best way of achieving this?
You can try react-dimensions
Wraps a react component and adds properties containerHeight and containerWidth. Useful for responsive design. Properties update on window resize. Note that the parent element must have either a height or a width, or nothing will be rendered
// ES2015
import React from 'react'
import Dimensions from 'react-dimensions'
class MyComponent extends React.Component {
render() (
<div
containerWidth={this.props.containerWidth}
containerHeight={this.props.containerHeight}
>
</div>
)
}
export default Dimensions()(MyComponent) // Enhanced component

Resources