How to nest the class of parent component in child component? - javascript

I'm in a learning phase of react and I've been trying to nest style of parent in component in child component via class in react.js. How to do it?
What I've tried until now:
function Card(props) {
const classes = 'card' + props.className;
return (
<div className={classes}>
{props.children}
</div>
)
}
export default Card
I applied style on class 'card' in a CSS file.
Parent component JS code:
import Card from './UI/Card';
function ExpenseItem(props){
return (
<Card className="expense-item">
<ExpenseDate date={props.date} />
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">{`₹ ${props.amount}`}</div>
</div>
</Card>
)
}
Now, there are various classes in parent component that are styled accordingly. And I'd like to nest those classes in child component for those style to work.
You can see in the code the way I tried to nest the classes, but it isn't working.
const classes = 'card' + props.className;
What am I doing wrong? And how should I correct it?

You missed a space after the card class name, otherwise i don't see any other issues in your code. Try below, hope it works
const classes = 'card ' + props.className;

Just one note, this will make your components dependent on each other. One of best things about React is that it allows you to have separation of concerns. So Card component is doing Card component staff, displaying some information and needs not be dependent on a parent in this case ExpenseItem.
One way to do it is to use styled-components.

Related

React - How to apply multiple values inside style

I have a certain component named ThemeDoc inside this component there is a context, localStorage, hooks, maps and whatnot, but in the end I get the value of the hooks inside a component named SideBar. I decided that there is no need to demonstrate the contents of the ThemeDoc component in order not to complicate my code and question, but I will attach a link of my project on the github if you want you can see GitHub Project. So as I just mentioned, I get the value of the hooks from the ThemeDoc component for the SideBar component, they look like this
const {SideBarValue, SideBarWallpaperValue, SideBarColor} = React.useContext(CounterContext);
const [SideBarTheme, SetSideBarTheme] = SideBarValue;
const [SideBarBackground] = SideBarWallpaperValue;
const [SideBarColorTheme, SetSideBarColor] = SideBarColor;
These values are used to change the color of the SideBar.
<div style={{background: SideBarTheme}}>
...
</div>
It all looks like this
Please see there are two values for SideBarTheme and SideBarColorTheme, SideBarTheme changes the background of the site to a gradient and the SideBarColorTheme only changes the SideBar color. As you can see I successfully applied the SideBarTheme value inside jsx and everything works fine for me
<div style={{background: SideBarTheme}}>
...
</div>
But I can’t use SideBarColorTheme, I don’t know how to do it, here is my full-fledged code if you need to tell me I will provide the ThemeDoc component
SideBar.jsx
function SideBar(props) {
const {someValue} = useContext(SideBarContext);
const {SideBarValue, SideBarColor} = React.useContext(CounterContext);
const [SideBarTheme, SetSideBarTheme] = SideBarValue;
const [SideBarColorTheme, SetSideBarColor] = SideBarColor;
return (
<div style={{background: SideBarTheme, backdropFilter: `blur(${someValue}px)`}}>
...
</div>
);
}

Looping a ReactJS component inside of its parent component

I have this component imported that returns a bunch of cards but I want to style this in a row instead of a column and it seems the way to do that is to loop the component as a <li> and then adding css but I cannot seem to make the component loop correctly.
the component looks like this inside of the parent element:
<div id="newsFeed" className='feed'>
<Feed theme={this.state.theme} articles = {this.state.articles} />
</div>
I have tried solutions such as:
var feedContent = <Feed theme={this.state.theme} articles = {this.state.articles} />
///////////////////////
{feedContent.map(item => <Feed key={item} value={item} />)}
</div>
but cannot seem to find any luck.
Any help would be greatly appreciated
map is a built in array method that is used a bunch in React. You need to use it on an array or else you will throw an error. I am assuming the value for articles is an array here:
//Feed component
class Feed extends React.Component {
constructor(props) {
super(props);
this.state = {
articles = []
};
}
componentDidMount = () => { // maybe call an API to populate the articles array }
render = () => {
return (
<ul className="someClass" >
{this.state.articles.map((item, index) => <li key={index} className="someOtherClass">{item}</li>)}
</ul>
);
}
}
alternatively you could create a li component, perhaps called FeedItem, import it and map it with the value prop from each item in the articles array.
// render method for Feed Component
render = () => {
return(
<ul className="someClass">
{this.state.articles.map((item, index) => <FeedItem key={index} value={item} />)}
</ul>
);
}
// FeedItem component
const FeedItem = ({value}) => {
return(
<li className="someOtherClass">{value}</li>
);
}
so, you are using map to create a list with the items in your articles array, and the map function loops through each article and returns a list component. Hopefully this helps! here's the React docs for reference: https://reactjs.org/docs/lists-and-keys.html
note: React docs advise against using an index as a key, but I don't know if your article array elements contain something unique. If they do (like an id of some kind), use that instead of index.
I think you need to change the approach.
I'd recommend you create an Article component, example:
function Article({ title }) {
<div>
{title}
<div>
}
After that, you might use the articles array to show each one:
//Feed component
this.state.articles.map((article, i) => <Article title={article.title}>)
In that way you can stylize the articles component as you want.
I was able to figure it out with simple CSS believe it or not. Basically <Feed /> was wrapped in a div in the parent component and was not responding to Flexbox CSS code, however, After I used changed the className of the div that was wrapping the component inside of its <div> within its own file the div responded to CSS! Thank you so much for your help everyone, I appreciate it!
Have you tried adding a className to the div that is the parent of the cards? Use that class to apply a flex display for example, and see what that gives you. If Newscard has a fixed width of 100% by chance, of course you'll need to adjust that to a small percentage / width to suit your needs.

How to force Antd to append element as child element of div React renders to instead of to HTML body?

Edit:
I figured it out & posted answer below.
Original Question
I am trying to create a completely compartmentalized web application within a shadow-dom and I've been using Antd components and ran into the issue where Antd is appending drop-down options into the body tag instead of as a child of the element that React is rendering into.
To isolate this issue I've removed everything outside of just React.render & a single Antd element which still does the same thing.
I then used a different component, "react-date-picker", which works how I had hoped Antd did, where the component renders as a child of the div specified for react.
Unfortunately, Antd rendering to the body of the HTML instead of as a child makes using shadow-root pointless.
Essentially my question is:
Is this Antd's normal functionality? If not then what might I be screwing up to have this happen? If so then is there a built-in Antd option that I'm missing that will render Antd options as child elements? If that option doesn't exist within their libraries, is there a way for me to force Antd to render as a child of my shadow-root node?
Here is what I'm using to render the Antd DatePicker component:
import ReactDOM from 'react-dom';
import React from 'react';
import DatePicker from 'antd/lib/date-picker';
ReactDOM.render(<DatePicker/>, document.getElementById('entry-fields'));
Before clicking on the Antd date picker:
After clicking on it, drop down options are appended to <body> and not <div id="entry-fields>:
Here is what I'm using to render the react-date-picker component to demonstrate the functionality I expected / need:
import ReactDOM from 'react-dom';
import React from 'react';
import DatePicker from "react-datepicker";
class Example extends React.Component {
state = {
startDate: new Date()
};
handleChange = (date: any) => {
this.setState({
startDate: date
});
};
render() {
return (
<DatePicker
selected={this.state.startDate}
onChange={this.handleChange}
/>
);
}
}
ReactDOM.render(<Example/>, document.getElementById('entry-fields'));
Before clicking on the react-date-picker date picker:
After clicking on the react-date-picker date picker (the drop down options are appended as children of the element react is rendered onto):
Basically I expected Antd to render its options encapsulated within the React rendered into <div></div> but it is instead appending elements on the <body></body>.
I'm relatively inexperienced in the web-dev domain and have resorted to asking a question here after way too much time trying to find the answer on my own. I am getting extremely frustrated in web-dev in general where any question seems to yield hundreds of irrelevant medium blog posts that are not useful in any capacity... assuming that it's not just me not knowing what to search for yet to find the answers I need which could very well be the case.
Really appreciate any help in advance.
Not sure how I managed to miss this but Antd has a parameter called "getCalendarContainer" which if left blank will render options into the body of the document but if supplied with the correct parameters will render the child elements into the container of your choosing.
Going off this example: https://react-component.github.io/calendar/examples/getCalendarContainer.html
I got it working by adding this function to my component:
getCalendarContainer()
{
return this.d || document.getElementById('calendarContainer');
}
and adding this to the component in JSX:
<div id="calendarContainer" ref={n => (this.d = n as HTMLDivElement)} >
<DatePicker onChange={EntryFields.onDateChange} getCalendarContainer={this.getCalendarContainer}/>
</div>
and initializing the div tag to reference it in the component's constructor like this:
private d: HTMLDivElement;
constructor(props: any)
{
super(props);
this.d = document.createElement("div");
...
It's also worth noting that the above will not work immediately when using shadow-DOM since you need to access the node that the shadow-DOM is a child to and then use getElementById().
Something along these lines (but probably better written I hope lol)
getContainer() {
let elem = null;
let shadowContainer = document.getElementById('entryFieldsShadow') as HTMLInputElement;
if (shadowContainer != null) {
let shadowDom = shadowContainer.firstElementChild;
if (shadowDom != null) {
let shadowRoot = shadowDom.shadowRoot;
if (shadowRoot != null) {
elem = shadowRoot.getElementById("entryFieldsContainer")
}
}
}
return elem || this.d;
}
where the JSX with react-shadow's shadow root is included looks like this:
return (
<div id="entryFieldsShadow">
<root.div>
<div>
<div id="entryFieldsContainer"/>
<style type="text/css"> #import "static/1.css"; </style>
<Layout>
<Content>
{this.RowCols()}
</Content>
</Layout>
</div>
</root.div>
</div>
)
This solve my problems
<DatePicker
{...}
getCalendarContainer={triggerNode => triggerNode.parentNode}
/>

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.

How to remove UI components using React JS

I'm having trouble finding relevant documentation on how to remove UI components when using react. For example, there's a login form. The user clicks submit and now the form should be removed from the screen. How do I do this?
I've found unmountComponentAtNode, but that can only be invoked at the parent level. Am I supposed to have a parent node that is aware of all child state and loads them conditionally? Should all children have an "isHidden" attribute which renders the dom as hidden if true?
This must be basic but I don't see this in the react js tutorials. I found this stackoverflow post (react.js: removing a component) is this really the pattern? It kind of makes sense but it means that a large app will likely have an extremely complex Application parent class that manages maps of application state based on configuration.
It seems like i need to start defining application state as named maps. For example:
BaseApp: showHeader=true;showContent=true;
LoginState: showBaseApp=true;showLoginForm=true;
LoggedInState: showBaseApp=true;showFeed=true;
At any moment we would have to update all state maps and call the base class render method...
In my opinion your question isn't about removing component but about showing the right component. And yes - it can be done with a component state but with Flux/Redux store/reducer as well.
In your example with a login form after click on "Submit" we can change local state for the component and show another text like "The request was sent blah-blah-blah" or another component.
But you can do this by extracting component's local state to a store/reducer and it'll be work better in relatively big app. Nevertheless, it's really up to you where you want to store state.
I like to use a hide prop like so.
class AppCtrlRender extends React.Component {
render() {
let page = this.state.appState.currentPage;
let hideAbout = (page != 'about');
let hideHome = (page != 'home');
return (
<div id='AppCtrlSty' style={AppCtrlSty}>
<div id='allPageSty' style={allPageSty}>
<AboutPage hide={hideAbout} />
<HomePage hide={hideHome} />
</div>
</div>
);
}
}
export default class AboutPage extends React.Component {
render() {
if (this.props.hide) return null;
let aTime = (new Cache()).time.toString();
return (
<div style={AboutPageSty}>
React 0.14 ReFlux used for app state. This is the About Page.
<NavMenu /><br/><br/>
{aTime}
</div>
);
}
}

Categories

Resources