Adjacent JSX elements wrapping with Fela and React issues with parent component - javascript

Adjacent JSX elements must be enclosed by a parent tag, which is causing me some issues with implementing Fela. I am new to all these technologies, and trying to ensure that I am applying them with best practices in mind. Say I have a Page component and a fela component -- pageWrapperCss :
const PageWrapperCss = createComponent(
(props) => (
{
paddingTop: props.navbarHeight + 'px',
display: 'flex',
flexDirection: 'column',
alignContent: 'stretch',
flexGrow: 1,
flexShrink: 0,
}
), 'div'
);
const Page = ({view}, {cssVars}) => {
// some logic here which may set TargetView = Dashboard component
return (
<PageWrapperCss {...cssVars} >
<TargetView />
</PageWrapperCss>
);
};
export default Page;
The TargetView component is composed of sections, which also have their css composed with Fela. Below are the sections wrapped by a parent node -- div. Therein lies the problem. Now I have a div node between my parent component -- PageWrapperCss -- which generates a div node with the attached classNames.
class Dashboard extends Component {
render() {
let { cssVars } = this.context;
return (
<div>
<SectionWrapperCss {...cssVars}>
<HeaderSection>
</SectionWrapperCss>
<SectionWrapperCss {...cssVars}>
<BodySection />
</SectionWrapperCss>
<SectionWrapperCss sectionFlex="1" {...cssVars}>
<AnotherSection />
</SectionWrapperCss>
</div>
);
}
}
Before I had PageWrapperCss component in the Dashboard component like the following, which worked fine; however I want to keep the Fela css logic within the component it relates to, so that I don't have to keep generating PageWrapperCss components in each target view.
class Dashboard extends Component {
render() {
let { cssVars } = this.context;
return (
<PageWrapperCss>
// sections
</PageWrapperCss>
);
}
}
How have others dealt with this? Fela is pretty flexible, I could just render the css in the Page component, and persist the className via props to all the views, and add them to the parent node.
class Dashboard extends Component {
render() {
let { cssVars } = this.context;
return (
<div className={this.props.pageCss} >
// sections
</div>
);
}
}
thanks!

Related

How do I keep the state of a React Component after removing others from an array?

I'm new to React and am not sure what I'm doing wrong here. I have a component called Blocks that contains an array of sub-components in state. Right now, when I add the sub-component Paragraph, I do so like this. This is in the parent component Blocks.
handleAddBlock(block) {
let new_block = null;
let last_block_id = this.state.last_block_id;
last_block_id++;
new_block = {
component: <Paragraph
key={last_block_id}
id={last_block_id}
/>,
id: last_block_id,
value: null
}
this.setState({ last_block_id: last_block_id });
this.setState({ blocks: [...this.state.blocks, new_block] });
}
The Paragraph component has a state variable "value", that is updated when a user types into a text box. However, when I go to remove an item from this.state.blocks, any components that come after the component I'm removing all get re-rendered, and lose their state. The components that come before the item I've removed keep theirs.The question is why, and how can I stop that from happening? Is this a bad design pattern?
Here's the code that handles the removal of a sub-component. This is in the parent component Blocks.
handleRemoveBlock(id) {
const blocks = [...this.state.blocks].filter(block => {
return block.id !== id;
});
this.setState({ blocks: blocks });
}
And finally, this is part of the render() method in the parent component Blocks.
render() {
const blocks = this.state.blocks.map(block => {
return <div
key={block.key}
className="col p-1"
>{block.component}
<button
className="delete-button"
onClick={() => this.handleRemoveBlock(block.id)}
type="button">X
</button>
</div>
})
return <section className="row">
<div className="col">
<div className="col">
{blocks}
</div>
</div>
</section>
}
I have a component called Blocks that contains an array of sub-components in state.
You shouldn't. Components should contain as little data in their state as possible. The main React design concept is that component's render method is a pure function of props and the state. Based on this, you should move <Paragraph/> instances (because you should render components only in render) and last_block_id (because it's computable from the blocks state) from state to render:
class Block extends React.Component {
handleAddBlock(block) {
const new_block = { ... }
this.setState('blocks', [...this.state.blocks, new_block])
}
get last_block_id() {
return this.state.blocks.at(-1).id
}
render() {
// your markup
return <...>
// create Paragraph here
{this.state.blocks.map(block => <Paragraph key={block.id} id={block.id} />)
<.../>
}
}

Are props passed from parent to child by default with `this`?

I am learning through an open source project here. I have deployed it and it works. So the below pasted code is valid for sure.
I was looking at a Header component in Header.js:
class Header extends React.Component {
state = {
open: false,
};
render() {
const {
classes,
toggleDrawerOpen,
margin,
turnDarker,
} = this.props;
return (
.... some code ....
)
I see that classes is passed as a prop from the parent. So I looked into the parent component, Dashboard. Here is the code:
import { Header, Sidebar, BreadCrumb } from './../../components';
import { toggleAction, openAction, playTransitionAction } from './../../actions/UiActions';
import styles from './appStyles-jss';
class Dashboard extends React.Component {
state = {
transform: 0,
};
componentDidMount = () => {
// Scroll content to top
const mainContent = document.getElementById('mainContent');
mainContent.addEventListener('scroll', this.handleScroll);
// Set expanded sidebar menu
const currentPath = this.props.history.location.pathname;
this.props.initialOpen(currentPath);
// Play page transition
this.props.loadTransition(true);
// Execute all arguments when page changes
this.unlisten = this.props.history.listen(() => {
mainContent.scrollTo(0, 0);
setTimeout(() => {
this.props.loadTransition(true);
}, 500);
});
}
componentWillUnmount() {
const mainContent = document.getElementById('mainContent');
mainContent.removeEventListener('scroll', this.handleScroll);
}
handleScroll = (event) => {
const scoll = event.target.scrollTop;
this.setState({
transform: scoll
});
}
render() {
const {
classes, // classes is here
route,
toggleDrawer,
sidebarOpen,
loadTransition,
pageLoaded
} = this.props;
const darker = true;
return (
<div className={classes.appFrameInner}>
// NOTE: Header component is here but I don't see how classes is passed to it.
<Header toggleDrawerOpen={toggleDrawer} turnDarker={this.state.transform > 30 && darker} margin={sidebarOpen} />
<Sidebar
open={sidebarOpen}
toggleDrawerOpen={toggleDrawer}
loadTransition={loadTransition}
turnDarker={this.state.transform > 30 && darker}
/>
<main className={classNames(classes.content, !sidebarOpen && classes.contentPadding)} id="mainContent">
<div className={classes.bgbar} />
</main>
</div>
);
}
}
You can see that the classes prop is passed from Dashboard's parent. However, I was expecting some syntax that shows it is passed into the child Header component.
See the "NOTE" line in the code, nothing was said about passing the entire props to Header component or passing the const classes specifically to Header.
How is classes passed from parent (Dashbaord) to child (Header)?
The classes prop is not passed from parent Dashboard to child Header.
The classes prop is made available directly to your Header component using the wrapping withStyles HOC when exporting your component:
export default withStyles(styles)(Header);
This approach is commonly known as CSS-in-JS and you can read more details in the material-ui styles documentation.

react-native : Can I use props in a method?

I have my class and I have a method and I am wondering if I could use props inside a mehtod.
Notice I try to use props in methodTwo. Is this possible? If not, is there a way I could use props in method?
import React from 'react';
import { Image, Text, View } from 'react-native';
export default class Test extends React.PureComponent {
methodOne = () => {
this.setState({
one:false,
two:false,
three:false
})
}
methodTwo = () => {
this.setState({
one:false,
two:false,
//I want to use props
three:this.props.three
})
}
render() {
return (
<View style={{ backgroundColor: 'transparent', alignItems: 'center' }}>
<Button title='one' onPress={()=>this.methodOne()}/>
// I could call i like this?
<Test three='newState'/>
</View>
);
}
}
methodTwo = () => {
this.setState({
one:false,
two:false,
three:this.props.three
})
}
props-> is the value that is been transferred from parent component to child component.
In class based component you fetch the value by using this.props.Attribute_name and in functional based component you can fetch the value using props.Attribute_name (mind functional based component dont have any concept of this)
if you want to use this.props.three ,then in parent component call (the component calling this particular component) <Test three="anyValue" /> then you can easily get this value in child component.
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class MouseWithCat extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/*
We could just swap out the <p> for a <Cat> here ... but then
we would need to create a separate <MouseWithSomethingElse>
component every time we need to use it, so <MouseWithCat>
isn't really reusable yet.
*/}
<Cat mouse={this.state} />
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<MouseWithCat />
</div>
);
}
}
The props are accessible to whole of the class scope with the syntax this.props.xxxx if you have passed it from its parent component. SO you can use in methodOne too.
You can use props inside a method. Any specific error you are facing ?.

Extend JSS style class instead of overwriting it

In my React app, I use React JSS for styling. Suppose I have these two files (skipping imports and another non interesting stuff).
This is App.js:
const styles = {
root: {
backgroundColor: '#ffffff',
},
header: {
backgroundColor: '#ff0000',
}
};
class App extends Component {
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
<Header classes={{ root: classes.header }}/>
</div>
);
}
}
export default withStyles(styles)(App);
and this is Header.js:
const styles = theme => ({
root: {
backgroundColor: '#0000ff',
padding: '1em',
},
});
class Header extends Component {
render() {
const { classes } = this.props;
return (
<header className={classes.root}>
Hello header
</header>
);
}
}
export default withStyles(styles)(Header);
What I would like to have is "overriding" the style of the root component of Header without overwriting it completely. I can do either of two things:
use <Header className={classes.header}/>, which results in the header element having the class App-root-0-1-2, which means the background is blue with the padding;
use <Header classes={{ root: classes.header }}/> (as above), which results in the header element having the class App-header-0-1-2, which means the background is read without padding.
It seems I can only have either the style defined by the component OR the one that the parent defines to override it. However, I would like to extend the internal style with the one passed by the parent - of course, with the parent taking precedence in conflicts. In this case, I wish to have the red background with the padding.
How can I achieve that? Is it impossible - do I need to pass the editable style as a property?
You can provide an external class name and use classnames (https://github.com/JedWatson/classnames) (or just inline them) to conditionally render this class name if present:
import classNames from "classnames";
const styles = theme => ({
root: {
backgroundColor: '#0000ff',
padding: '1em',
},
});
class Header extends Component {
render() {
const { classes, className } = this.props;
return (
<header
className={classNames({
[classes.root]: true,
[className]: className
})}>
Hello header
</header>
);
}
}
export default withStyles(styles)(Header);
Then use it:
<Header className={classes.myParentClass} />
This will result in a class names, e.g. Header-root-0-1-2 App-myParentClass-0-4-3

In react, how to get noticed when children change?

I am making this class called Scrollable which enables scrolling if the width/height of the children elements exceeds a certain value. Here is the code.
import React, { Component } from 'react';
const INITIAL = 'initial';
class Scrollable extends Component {
render() {
let outter_styles = {
overflowX: (this.props.x? 'auto': INITIAL),
overflowY: (this.props.y? 'auto': INITIAL),
maxWidth: this.props.width || INITIAL,
maxHeight: this.props.height || INITIAL,
};
return (
<div ref={el => this.outterEl = el} style={outter_styles}>
<div ref={el => this.innerEl = el}>
{this.props.children}
</div>
</div>
);
}
};
export default Scrollable;
// To use: <Scrollable y><OtherComponent /></Scrollable>
This works great. Except now I wish to add one more functionality which makes the scrollable always scroll to the bottom. I have some idea of how to do it:
this.outterEl.scrollTop = this.innerEl.offsetHeight;
But this only need to be called when this.props.children height changes. Is there any idea on how to achieve this?
Thanks in advance.
I would recommend a package element-resize-detector. It is not React-specific but you can easily build a high-order component around it or integrate your Scrollable component with it.
Now I have an idea of solving this.
Since I am using react-redux. The problem is that I could not use lifecycle hooks on this Scrollable component since this.props.children might not necessarily be changed when the content is updated.
One way to achieve this is to make Scroll component aware of the corresponding values in the redux state. So that when that relevant value is updated, we can scroll down to the bottom.
Scrollable component:
import React, { Component } from 'react';
const INITIAL = 'initial';
class Scrollable extends Component {
componentWillUpdate(){
if(this.props.autoScroll){
// only auto scroll when the scroll is already at bottom.
this.autoScroll = this.outterEl.scrollHeight - this.outterEl.scrollTop - Number.parseInt(this.props.height) < 1;
}
}
componentDidUpdate(){
if(this.autoScroll) this.outterEl.scrollTop = this.outterEl.scrollHeight;
}
render() {
let styles = {
overflowX: (this.props.x? 'auto': INITIAL),
overflowY: (this.props.y? 'auto': INITIAL),
maxWidth: this.props.width || INITIAL,
maxHeight: this.props.height || INITIAL,
};
return (
<div ref={el => this.outterEl = el} style={styles}>
<div ref={el => this.innerEl = el}>
{this.props.children}
</div>
</div>
);
}
};
export default Scrollable;
Scrollable container:
import { connect } from 'react-redux';
import Scrollable from '../components/Scrollable';
const mapStateToProps = (state, ownProps) => Object.assign({
state: state[ownProps.autoScroll] || false
}, ownProps);
export default connect(mapStateToProps)(Scrollable)
With this, Scrollable's life cycle hooks will be called when the corresponding state changes.

Categories

Resources