prevent remount when component is wrapped in parent - javascript

I have a component that may or may not be rendered in a wrapper component:
class Video extends Component {
state = { isFullscreen: false }
render () {
const { isFullscreen } = this.state
return (
<View>
{isFullscreen ? (
<Modal>
<VideoView />
</Modal>
) : (
<VideoView />
)}
<Button title='inline' onPress={() => this.setState({ isFullscreen: false })} />
<Button title='fullscreen' onPress={() => this.setState({ isFullscreen: true })} />
</View>
)
}
}
Every time I press inline or fullscreen, the <VideoView /> remounts. This makes it hard to add initialization logic to the componentWillMount method. I can do other checks in my application to make sure it works well, but it feels better to re-use the component that already exists.
Is there a way to recycle the <VideoView />?
Ps My project is in React Native, so I used some syntax / component from RN, but I assume the question goes for normal React projects as well

Try wrapping the component with a wrapper and now toggle the styles for this wrapper based on state change. I guess that should work.
Also if you are imminent on reusing the modal component only, what you can do is define some CSS under a new className on the modal Component that alters the required styling to give an effect that the modal is toggled. Then add remove the className based on the state change.

Related

React children count

i have a component like this
<Slider
Arrows
Autoplay
>
<SliderContentMall />
</Slider>
and in SliderContentMall i make a map like this
const SliderContentMall = (props) =>
{
return (
props.Data.map(DataValue =>
{
return (
<TouchableOpacity style={Styles.ImageContainer} onPress={() => {}>
</TouchableOpacity>
)
})
);
}
this map returns 3 object the things is in the slider component when i do
console.log(React.Children.count(this.props.children))
it only return 1 and i need the count of the 3, i cant make the map in the slider directly the map needs to be in the SliderContentMall
React.Children.count is doing what it's intended to do: there's a single child, and that child is a <SliderContentMall>. It's not going to check how many children are underneath that, and in fact it can't since those children havn't been rendered yet. Rendering goes from top down, so while you're rendering the <Slider>, you have not yet rendered the <SliderContentMall>, and thus the various <TouchableOpacity>'s havn't even been thought of yet.
If the slider needs to know how many pieces of data will be rendered, the most straightforward way to do that is as a prop to the slider. For example:
<Slider
Arrows
Autoplay
count={data.length}
>
<SliderContentMall data={data}/>
</Slider>
You are using map function over props.Data instead of props.children.

Reuse React component from render props

I wrote a react component in render props way,it will call children function with 3 react component object ( not sure the name exactly, the variable generated by executing jsx (<div>...</div>) );
<PaginatedTable> Usage example:
<PaginationTable data={data} ...otherprops>
{({ SearchBar, Table, PaginationBar })=>
(<div>
{SearchBar}
{Table}
{PaginationBar}
</div>)
}
</PaginationTable>
with render props, I'm so glad that I can custom these 3 child component object very easily such as rearrange order or adding custom elements between these three.
{({ SearchBar, Table, PaginationBar })=>
(<div>
{PaginationBar}
<h1> my custom search bar text </h1>
{SearchBar}
{Table}
</div>)
}
But now I wish more than arrange order inside , I wish I can move {SearchBar} out of to the same layer of 's sibling 's children such as this picture.
working demo: https://codesandbox.io/s/23q6vlywy
I thought this may be anti-pattern to the unidirectional data flow of React.
Extract {SearchBar} to another independent component then use it as <SearchBar ... /> inside of <ToolBarArea /> is what I learnd from React Doc.
But in this way, I have to do "lifting state up" and write similar function and states already had in <PaginationTable /> like below **text** parts are functions and states already had in <PaginationTable />
class ToolBarArea extends Component{
render(){
return(
<div>
// text
<SearchBar onChange={**this.props.onSearchBarChange**} />
//.... other text or elements
</div>);
}
}
class ContainerArea extends Component {
state={
**searchBarText:'',**
tableData : [{...}, {...}]
}
**onSearchBarTextChange = (event)=>{
this.setState({ searchBarText: event.target.value });
}
filterdTableData = ()=> this.state.tableData.filter(d=>d.name.includes(this.state.searchBarText);
**
}
I really hope there is a way I can simply move the variable {SearchBar} in the render props function out to without knowing is in the parent or parent's sibling or anywhere in the DOM tree.
such as
<ToolBarArea>
{SearchBar} // SearchBar from <PaginationTable />
</ToolBarArea>
Is there a way to reuseonSearchBarTextChange and filtedTableData functions and these **text** codes I already wrote in <PaginationTable /> ?
I believe you hit the nail on the head when you referred to lifting state. If you already wrote a similar function then your best option may be to 'abstract' that function so that it applies to both use cases. You could use a simple flag to differentiate the unique execution each needs. Then finally pass the function down to both components.
If you're adamant about avoiding this approach you could technically get around it by using event listeners to handle data transfer or watch variables in the window but this is for sure an anti-pattern.

Is it ok to render a React element multiple times?

Let's say I have a functional component rendering 5 icons.
This can be done like this:
export default const Icons = () =>
<div>
<Icon/>
<Icon/>
<Icon/>
<Icon/>
<Icon/>
</div>;
But it can also be done like this:
const icon = <Icon/>;
export default const Icons = () =>
<div>
{icon}
{icon}
{icon}
{icon}
{icon}
<div/>;
The difference is that in the second case, only one React element is created using React.createElement.
Is it discouraged to render a React element multiple times?
If so, why is that? If not, are there any performance or size benefits to be had by rendering a React element multiple times?
You're thinking it wrongly. Both are the same. There's no difference between them. In the second case, you're just assigning to component <Icon /> and each time you call {icon} will actually call to the component <Icon />.
I would rather choose to use first. Because, I don't need to assign unnecessarily which calls to the component.
Here's what will happen when using second case:
Look for {icon}
Look for assigned component <Icon />
Render <Icon /> component
Here's what will happen when using first case:
Look for <Icon />
Render <Icon /> component
It is ok. You can also map over some array of data that you can use to pass down to your components as props.
const altTags = ['me', 'dog', 'cat'];
const icon = <Icon/>;
export default const Icons = () =>
<div>
{this.altTags.map(tag => <Icon alt={tag} />)}
</div>;
It is ok but is not really efficient from the architectural point of view.If we follow the principles of SOLID development this break a few rules.
From a maintenability and reusability point of view this can be improved.
If you create a property in the component that specify how many icons you want and in the render method you use a loop to iterate and create the icons this code will be much better.

How to Get: Component Width After Render in React

I'm trying to create a general purpose component, that I can reuse in other applications. I need to know the width of the component after render so that I can modify the content of the component.
I've been trying to use the different life cycles in react without success.
componentDidUpdate() {
console.log('width', this.element.offsetWidth);
}
render() {
return (
<div ref={(element) => {this.element = element }} />
);
}
When I try this I get the width of the screen, but if I change the size of the window, I get the width of the component. See the Chrome Log:
ComponentDidMount executes before render so this.element is undefined.
I've also attempted to use different libraries from npm to solve this without luck.
Futher information: The component has to work inside a Bootstrap column, at different widths.
render() {
<Row>
<Col sm={3} />
<MyComponent />
</Col>
<Col sm={9} />
<MyComponent />
</Col>
<Row>
}
Clarification I do not want to resize the window, and I apologize for not being clear. The only reason for me to mention the resizing is that when the DOM has been created and I resize, I get the correct value in offsetWidth. I'm looking for a solution where I get the correct value without resizing. Either a post render function call, listeners, some react magic, or other solutions. My problem is my lack of knowledge with the virtual vs. real DOM.
I was unable to solve this problem with the answers given here. I only got the width of the browser window and not the component within. After some research, it looks like I'm having a chicken or the egg problem with the render. After some more research, I found react-sizeme that solves the issue.
import React, { Component } from 'react';
import sizeMe from 'react-sizeme';
class MyComponent extends Component {
render() {
const { width } = this.props.size;
return (
<div style={{
width: '100%',
backgroundColor: '#eee',
textAlign: 'center'
}}>
<span>My width is: {Math.floor(width)}px</span>
</div>
);
}
}
export default sizeMe()(MyComponent);
Which will produce the following when it first renders
If you need to hold component width state you can do something like this:
componentDidMount(){
this.boundingBox = this.element.getBoundingClientRect();
this.setState({
width:this.boundingBox.width
});
Observable.fromEvent(this.element,"resize")
.subscribe(
() => {
this.boundingBox = this.element.getBoundingClientRect();
this.setState({
width:this.boundingBox.width
});
}
);
};
You can replace Observable with event listener.
Alternatively you can update bounding box attached to class and derive state from it somewhere else.
componentDidUpdate(){
this.boundingBox = this.element.getBoundingClientRect();
};
Whereas this is not an answer to your question directly, it's a solution to your problem that does not add resizing and dirty logic inside of your components.
I'd recommend something like https://github.com/digidem/react-dimensions - which is a HOC wrapper and will listen to global resize events and send you props - containerWidth and containerHeight - I tend to use it a lot when working with SVG, canvas and data grids in react that need to remain responsive and need to know the element's size.
As for lifecycles - things like componentDidUpdate may not behave the way you think it should. Mount is a one-off. Read this comment - https://github.com/facebook/react/issues/2659#issuecomment-66165159
This "SizeMe" component helped me dynamically resize my d3 chart, thanks.
In case it may help someone, this my code from the calling Parent component:
import { SizeMe } from 'react-sizeme'
:
<SizeMe monitorWidth>
{({ size }) => (
<MyChartComponent
divChart = "my_chart_div"
chartWidth ={parseInt(size.width)}
someProps ={my_obj.some_data}
/>
)}
</SizeMe>
With the CSS:
#my_chart_div {
width: 100%;
}

React Native Specifying Style for Sub-Compoennts

I have the following component:
class Dashboard extends Component {
render() {
return (
<Text>MAIN DASHBOARD</Text>
);
};
}
Which I am declaring as:
<View style={styles.dashboard}><Dashboard/></View>
When I declare my styles I have:
dashboard: {
flex:3
},
Now, assume that I wanted to be able to style different sub-components in different ways, for example having my Dashboard component return two views:
<View class="a"><Text>View A</Text>
<View class="b"><Text>View B</Text>
What I'd like to achieve is a css equivalent of:
Dashboard>View.a - {/*props of views with class 'a' which are children of a dashboard element/*}
Dashboard>View.b - {/* Same but for class b /*}
Is there any way to achieve this?
The React Native style system doesn't really have the concept of classes and cascading styles, as in your example. If you are in control of all the child components, you can simply pass arbitrary style objects to your parent Dashboard component and then pass those to the children:
<Dashboard aStyle={{}} bStyle={{}} />
And in your Dashboard view:
return (
<View>
<AView style={this.props.aStyle} />
<BView style={this.props.bStyle} />
</View>
)
For more flexible theming support, you could also use the styled-components library, which allows you to specify a Theme for a certain subtree of your application, and then consume those theme values in any of the children.
That, however, might be overkill for your use case, and you can get by with a simpler solution.

Categories

Resources