styled-components won't work with multiple ReactDOM.render - javascript

So I'm building a RubyOnRails application with React in the frontend. I have several microfrontends and I render each of them separately. Like this:
index.html.erb
<div id="app-header" />
<div id="app-content" />
Header.jsx
import React from 'react';
import styled from 'styled-components';
import ReactDOM from 'react-dom';
const StyledHeader = styled.div`
// ...
`;
const Header = () => {
return <StyledHeader>...</StyledHeader>;
}
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(
<Header />,
document.getElementById('app-header'));
});
App.jsx.jsx
import React from 'react';
import styled from 'styled-components';
import ReactDOM from 'react-dom';
const StyledApp = styled.div`
// ...
`;
const App = () => {
return <StyledApp>...</StyledApp>;
}
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(
<App />,
document.getElementById('app-content'));
});
The problem is that it looks like styled-components one renders the last styles from ReactDOM.render.
So in this case only app-content would be styled. If I remove it, than app-header is styled. I inspected the elements and it looks like the styles from app-header are not in the DOM when app-content is present. It's like it gets overridden.
I can't make the content and the header under the same root component because the content is not always React.
Is there a way of making the styles work without breaking this structure?

Related

Style child component from parent component using styled-components

I have a parent component and a child component. I import child component into parent and use it there. Below is the child component.
import styled from "styled-components";
const HeaderContainer = styled.h1``;
const Header = () => <HeaderContainer>This is Header</HeaderContainer>;
export default Header;
As you can see it is a simple component all it does it render text.
The parent component is below.
import "./styles.css";
import Header from "./header";
import styled from "styled-components";
const Main = styled.div`
${Header} {
background-color: "red";
}
`;
export default function App() {
return (
<Main className="App">
<Header />
</Main>
);
}
I am importing Header and using it inside JSX. What I want is to style Header component from parent component. I tried below styles using styled-components but it does not work somehow.
const Main = styled.div`
${Header} {
background-color: "red";
}
`;
Here is the codesandbox.
https://codesandbox.io/s/dreamy-brown-w3bbs?file=/src/App.js:0-277
How can I make this work or if you have a better idea then please share.
You don't need any global class names or selecting DOM elements which are both obvious bad practices. Instead, just use "styled" on the child component inside its parent. So, e.g., in your case:
import Header from "./header";
import styled from "styled-components";
const StyledHeader = styled(Header)`
background-color: "red";
`;
export default function App() {
return (
<Main className="App">
<StyledHeader />
</Main>
);
}
Also it's important to notice that: Some CSS-in-JS tools will require you to manually add a className prop inside the child component's props in order for this to work. So, e.g., your Header component must become:
const Header = ({ className }) => <HeaderContainer className={className}>This is Header</HeaderContainer>;
You don't need to pass this prop yourself from the parent component, your CSS-in-JS tool will pass this className automatically.
Hopefully I'm following what you want to do but in your Main styles you could just target the h1 for the header?
const Main = styled.div`
h1 {
background-color: red;
}
`;
Or if you didn't want to target all H1's then add a className to your header, I've added a few ways to do this here: https://codesandbox.io/s/clever-stallman-bdwfk?file=/src/App.js
In your code, the part where you tried to style the header, it was wrong. Instead what you can and should try is:
inline styles:
import "./styles.css";
import Header from "./header";
import styled from "styled-components";
export default function App() {
return (
<Main className="App">
<Header style={{background-color: "red"}}/>
</Main>
);
}
External CSS
You can give a class name to your elements inside the components and style them in the parent file like the following
The css file, let's name it style.css
.header-heading {
font-size: 4rem;
color: "red";
}
The child component
const Header = function () {
return (
<h1 className="header-heading">This is the heading</h1>
);
}
export default Header;
Then you can import both of them in the parent file and you'll see that the child component is styled now.
The parent component
import React from "react";
import Header from "./Header";
import "./style.css";
const App = function () {
<>
<h1>Below is the h1 component rendered</h1>
<Header />
</>
}

Problem with exporting/importing React component

I'm trying to create a React component for a navigation bar.
This component I'd like to import from a separate file into my App.js.
Currently, the component should just return a simple 'Hello world' paragraph, but I have trouble getting this to work.
I have written the following code into a file located at src/components/navbar.js:
import React from 'react';
export default class navBar extends React.Component {
render() {
return (
<p>Hello world.</p>
)
}
}
Now I'd like to import this component from my src/App.js, which looks like this:
import React, { Component } from 'react';
import './App.css';
import navBar from './components/navbar.js'
class App extends Component {
render() {
return (
<navBar/>
);
}
}
export default App;
If I compile and open the site, nothing's there, which confuses me.
I'd be very thankful for any help!
EDIT:
It's been suggested that the problem is that <App /> is not being rendered anywhere. I don't believe that's the case, since there's another file being created by default (index.js), which looks like this:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
I have also tried putting the paragraph (and the entire navbar) directly into src/App.js.
After compiling I could see the expected results in the browser, so the problem should lie with the exporting/importing.
In JSX, lower case tags are considered to be simple HTML/SVG elements. You can use lower case only if you use accessors (so with a dot like bla.blabla).
You can read about it here for example.
So in your case you must change the class name navBar to NavBar and then in the render method:
render() {
return (
<NavBar/>
);
}
Here is a full working example:
** Note: NavBar.js shoud start with a Capital letter.
App.js
import React from "react";
import ReactDOM from "react-dom";
import NavBar from "./components/NavBar";
function App() {
return (
<div className="App">
<NavBar />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
NavBar.js
import React from "react";
export default class NavBar extends React.Component {
render() {
return (
<div>
<p>Hello world.</p>
</div>
);
}
}

Using custom tag to reference ReactJS components inside ReactJS application

I have a ReactJS basic project which is working fine with the following two files (among others):
Header.js
import React from 'react'
const Header = () => (
<div>THIS IS A HEADER</div>
)
export default Header
App.js
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Header from './Header'
class App extends Component {
render() {
return (
<div>
<Header />
<div>
Hello World! This is the content.
</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
What I want to know is:
How can I reference the Header component with a custom tag, for example: <comp-header /> instead of: <Header />. Something like below:
App.js
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Header from './Header'
class App extends Component {
render() {
return (
<div>
<comp-header />
<div>
Hello World! This is the content.
</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
Any idea on how to do that?
Thanks!
You can simply
import CompHeader from './Header'
and use normally.
I don't think you can use a dash in JSX and JSX elements that are non native elements need to be capitalized.

React Css Themr not working

My target is to have one main theme for the app and load custom vendor styles if one is set.
I have been following the tutorial of react-css-themr and I can't get it to work. The minimalistic example I could come up with is this:
my module:
import {render} from 'react-dom'
import React from 'react';
import {Item} from './components/presentational/Item';
import {ThemeProvider} from 'react-css-themr';
import style from './theme/ItemDefault.scss';
const contextTheme = {
Item: require('./theme/ItemVendor.scss'),
};
const About = () => {
return (
<ThemeProvider theme={contextTheme}>
<Item theme={style} className={style.red}/>
</ThemeProvider>
)
};
ItemDefault.scss:
.button{
color:deeppink;
}
ItemVendor.scss:
.button{
color:orangered;
}
That doesn't seem to give my any classes or any styling. Any ideas please?
The way I was wiring components was incorrect. The way to do this is as follows:
In the root component you need to have your theme provider and theme attached to it. This theme will override any child component theming.
import {render} from 'react-dom'
import React from 'react';
import {ThemeProvider} from 'react-css-themr';
import inlineCss from './page.scss';
import {Item} from './components/Item';
const contextTheme = {
Item: require('./theme/ItemVendor.scss'),
};
render((
<ThemeProvider theme={contextTheme}>
<Item />
</ThemeProvider>
), document.getElementById('app'));
The component itself will have it's default theming and then will be wrapped with themr API to overwrite the it's default settings.
import {render} from 'react-dom'
import React from 'react';
import { themr } from 'react-css-themr';
import defaultTheme from './Item.scss';
const DefaultItem = ({theme}) => {
return (
<div className={theme.button} >
Example item
</div>
)
};
export const Item = themr('Item', defaultTheme)(DefaultItem);
I put together a github repo showing how to use this:
https://github.com/adamgajzlerowicz/react-css-themr

Additional component in <MuiThemeProvider /> results in blank page w/ no error messages

I have recently installed Material UI into my Meteor application using npm install --save material ui
I have gotten the <Header /> component showing up in my app.js file, but whenever I add other components, localhost:3000 simply displays a blank page. Please see my code below:
header.js
import React, { Component } from 'react';
import AppBar from 'material-ui/AppBar';
class Header extends Component {
render() {
return(
<AppBar
title="Header"
titleStyle={{textAlign: "center"}}
showMenuIconButton={false}
/>
);
}
}
export default Header;
app.js
import React from 'react';
import ReactDOM from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import Header from './components/header';
import NewPost from './components/new_post';
const App = () => {
return (
<MuiThemeProvider>
<Header />
</MuiThemeProvider>
);
};
Meteor.startup(() => {
ReactDOM.render(<App />, document.querySelector('.render-target'));
});
THE ABOVE CODE WORKS WELL (see screenshot below)
However, if I add another component I get a blank screen
header.js is the same
new_post.js
import React, { Component } from 'react';
import TextField from 'material-ui/TextField';
class NewPost extends Component {
render() {
return (
<TextField
hintText="Full width"
fullWidth={true}
/>
);
}
}
export default NewPost;
app.js
import React from 'react';
import ReactDOM from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import Header from './components/header';
import NewPost from './components/new_post';
const App = () => {
return (
<MuiThemeProvider>
<Header />
<NewPost />
</MuiThemeProvider>
);
};
Meteor.startup(() => {
ReactDOM.render(<App />, document.querySelector('.render-target'));
});
The result is simply a blank screen
Why does adding one more component (<NewPost />)inside of <MuiThemeProvider> result in a blank screen? I referred to the material-ui documentation and their sample projects but their application structure is not similar to mine. Any advice? Please let me know if you need more info to make this question clearer.
Wow very strange but I managed to get it working by simply adding a <div>
app.js
const App = () => {
return (
<MuiThemeProvider muiTheme={getMuiTheme()}>
<div>
<Header />
<NewPost />
</div>
</MuiThemeProvider>
);
}
Meteor.startup(() => {
ReactDOM.render(<App />, document.querySelector('.render-target'));
});
I would really appreciate if anyone could explain why adding a div makes this all work. Thank you!
I would really appreciate if anyone could explain why adding a div
makes this all work
If you look at the browser warning, "Invalid prop children of type array supplied to MuiThemeProvider, expected a single ReactElement.".
So, when you add a <div/> around your components, it wraps them together and turns them into a single react element.
MuiThemeProvider renders as null so you have to wrap children do anything - for example React.Fragment

Categories