I got a requirement from my client that we need to have custom themes in our react application.
Where user can select one of the available themes of his choice which will change some css attribute like font-color, font-family etc.
Right now I apply css rules to my application using className attribute.
I tried searching some of the ways of implementing the same and found one way that is having alternate css
<link rel="stylesheet" href="main.css">
<link rel="stylesheet alternate" href="light.css" id="light" title="Light">
<link rel="stylesheet alternate" href="dark.css" id="dark" title="Dark">
<script>
function enableStylesheet (node) {
node.rel = 'stylesheet';
}
function disableStylesheet (node) {
node.rel = 'alternate stylesheet';
}
</script>
And toggle rel value based on user selection.
Can any one suggest me any other way of achieving the same.
Thanks in advance.
Your usage is the correct way of implementing. But what if you've numerous theme? Loading all the css files will hamper the website. So, I would suggest you to update the source instead of loading all css files and enabling/disabling them.
node.ref = 'dark.css';
This way, you're requiring the css file only when in use. Once, the file is being used, next time it's cached. So no need to worry about them later. Obviously, it might take some time and may impact on performance if the filesize is huge, at initial use. Even though the best thing is that you don't need to wait for all.
But wait!!!
You're using react. React provides us context api and I would obviously utilize this in such scenario. Here's an excerpted example:
import {ThemeContext, themes} from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';
class App extends React.Component {
constructor(props) {
super(props);
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === themes.dark
? themes.light
: themes.dark,
}));
};
// State also contains the updater function so it will
// be passed down into the context provider
this.state = {
theme: themes.light,
toggleTheme: this.toggleTheme,
};
}
render() {
// The entire state is passed to the provider
return (
<ThemeContext.Provider value={this.state}>
<Content />
</ThemeContext.Provider>
);
}
}
function Content() {
return (
<div>
<ThemeTogglerButton />
</div>
);
}
ReactDOM.render(<App />, document.root);
Similar concept can be implemented using redux and with logger information to make our work easy.
Related
Im new to nextjs, and Im checking if it will be good for the app that will have pretty complex and messy internal navigation. Just checked their documentation and I see that they recommend usage
of Link component like this <Link href="/your_path">Path</Link>. A bit scary is that I have to provide 'your_path' as a string so every time i change page file name I have to manually update code that redirects to this page. Is there any solution that allows me to define routing on my own so I can write something like (pseudocode)
routes = [
...
{
page : 'page_name',
path : 'path_to_page'
}
...
]
So instead of using string I can do <Link href="{route.path}">Path</Link> or Im condemned to use this file-system based router with all consequences?
The simple answer is yes!
When you want to change a user route in NextJs you have 2 options,
The first is with the <Link> Element that you can specify a href to where it directs.
And you also have a useRouter hook for more complex routing for example if the user does an action that requires moving him into a different route you can do it internally in your handlers.
For more information about useRouter hook.
What I usually do is storing my routes in an object
const ROUTES = {
HOME: "/",
ABOUT: "/about"
}
and wherever you call routes you just use the object so F.E
With Link tag
<Link href={ROUTES.ABOUT}>ABOUT PAGE</Link>`
with useRouter hook
// Inside a React Component
const router = useRouter();
const handleNavigateToAbout = () => {
router.push(ROUTES.ABOUT);
}
return (
// SOME JSX
<button onClick={handleNavigateToAbout}> Go to about page! </button>
)
I am building a site with Gatsby.
I am using a component that imports a script and returns a form.
The problem is, that after you loaded the page that shows the form, and then you click to any other page and go back to that form page, the css fully crashes for the entire site and you have to refresh the whole page.
To check out what I mean click this link https://baerenherz.org/, go to the dark blue button on the very right of the menu, then click to any other navigation site and then click again on the blue button (jetzt-spenden).
Here is my component for the donation form :
import React, { useState, useEffect } from "react"
import {Helmet} from "react-helmet"
import Loading from "./Loading"
function Child() {
return(
<div style={{width: "75%", margin: "4em auto"}} >
<Helmet>
<script type='text/javascript' aysnc>
{` window.rnw.tamaro.runWidget('.dds-widget-container', {language: 'de'}) `}
</script>
</Helmet>
<div className="dds-widget-container"></div>
</div>
)
}
function RaiseNow() {
const [loaded, setLoaded] = useState(false)
useEffect(() => {
const scriptTag = document.createElement('script')
scriptTag.src='https://tamaro.raisenow.com/xxx/latest/widget.js'
scriptTag.addEventListener('load', ()=> setLoaded(true))
document.body.appendChild(scriptTag)
return ()=>{
scriptTag.removeEventListener(); // check if necessary
setLoaded(false) // check if necessary
}
}, []);
return (
<>
{loaded ? <Child /> : <Loading/>}
</>
)
}
export default RaiseNow
What I noticed is, that the second time you visit the page, the Loading.... component does not even show anymore.. the Layout is displayed but as soon as the form shows, it crashes...
Since I cannot solve this issue since literally last year I would really appreciate any help with this. Thank you in advance.
Apparently, your script is breaking React's hydration when the component should be mounted/unmounted. There's no "clean" solution if there's no React-based script available. The problem here is that your script is manipulating the DOM while React manages the virtual DOM (vDOM). Changes in the DOM outside React's scope are not listened to by React and vice versa.
That said, I'd try forcing the loading and rendering of your widget each time the page loads. Something like:
function RaiseNow() {
const [loaded, setLoaded] = useState(false)
useEffect(() => {
const scriptTag = document.createElement('script')
scriptTag.src='https://tamaro.raisenow.com/xxx/latest/widget.js'
scriptTag.addEventListener('load', ()=> setLoaded(true))
document.body.appendChild(scriptTag)
window.rnw.tamaro.runWidget('.dds-widget-container', {language: 'de'})
return ()=>{
scriptTag.removeEventListener('load', setLoaded(false)); // check if necessary
setLoaded(false) // check if necessary
}
}, []);
return (
<>
{loaded ? <Child /> : <Loading/>}
</>
)
}
export default RaiseNow
Without a CodeSandbox it's difficult to guess how the code will behave but what it's important is to detach and clean up the listeners when the component is removed from the UI to avoid breaking React's hydration process, in the return statement. From the useEffect docs:
The clean-up function runs before the component is removed from the UI
to prevent memory leaks. Additionally, if a component renders multiple
times (as they typically do), the previous effect is cleaned up before
executing the next effect. In our example, this means a new
subscription is created on every update. To avoid firing an effect on
every update, refer to the next section.
There, besides removing the listeners from the script, you can also set the loading state to false.
I've also removed the second useEffect because the idea to avoid the CSS breaking is to force the loading of the script in each page rendering. It's not an optimal solution but it may work for you. The ideal solution would be using React-based dependencies.
Another thing to take into account is to delay the trigger of your rnw.tamaro script until the DOM tree is loaded, by moving it from the Helmet to the useEffect. This should ensure that your div and the window are available.
Turns out it was a issue on their end. Since they did an update it works.
I'm totally new with next.js and I need your help for something I guess really basic but I cannot find my mistake or an explanation, I found nothing on the internet about it, so here I am :
Everything works when I create a file in the pages folder(I mean every file in pages folder is ok except _app.js or _document.js), I can reach the URL, but I would like to use context, layout or authentification in the future and I need to use the _app and _document override cool things but I can write anything I want in it, it seems my _app.js or _document.js are just useless, never called or I don't know but they just never work.
I tried on 2 projects, here is what I do according to the next documentation :
first, npx create-next-app to create the project, and then add an _app.js for example in pages folder and add :
import React from 'react'
import App from 'next/app'
import Nav from '../components/nav'
class MyApp extends App {
// Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.
//
// static async getInitialProps(appContext) {
// // calls page's `getInitialProps` and fills `appProps.pageProps`
// const appProps = await App.getInitialProps(appContext);
//
// return { ...appProps }
// }
render() {
const { Component, pageProps } = this.props
return (
<>
<Nav />
<Component {...pageProps} />
</>
);
}
}
export default MyApp
Anybody could tell me what I am doing wrong?
Well, if anybody is going through the same issue, I found what was going on, in fact, after creating for the first time _app.js, I have to restart my docker container, or restart my app with yarn next dev if I want to see the changes or they never appear. I am going to look for more explanations on how SSR and next.js more globaly exactly work to understand their behaviour on this point. Good luck all !
We currently have our website built with Wordpress and I have built a component in React that we want to conditionally render on the page.
I have the component built and bundled with Webpack for production but cannot figure out how to add it to our website and render the component on the page
edit I will also need to conditionally render the component (form submit, button click, etc.)
Has anyone successfully done this?
Since you've built and bundled your component already, you only need to do three things:
Add the bundled JavaScript (the Webpack output) as a script tag in whatever page you want the component to be on (preferably at the bottom of the <body>).
Example: <script src="wp-content/react/bundle.js"></script>
Add the root element that React will bind to to the same page.
Example: <div id="react-root"></div>
In your component file, render the component to the root element.
Example: ReactDOM.render(<Component />, document.getElementById('react-root'));
To dynamically show and hide the React component based on something that happens outside the component file, you need to create a way to connect the outside (the DOM) to the inside (React). The easiest way to do this is with a global variable attached to window.
In your React component, add the componentWillMount method that defines a global variable:
componentWillMount() {
window.showComponent = (option) => {
// "option" should be true or false
this.setState({ display: option });
}
}
Based on the value passed to setState above, you'll need to add the display property to your component's state:
constructor(props) {
super(props);
this.state = {
display: false
};
}
Now to make the component show or hide based on the value of this.state.display inside the render() method:
render() {
if (this.state.display) {
return (
...
)
} else {
return null;
}
}
All that's left to do is use showComponent(true) or showComponent(false) in your code that handles the form.
In your React project add the file to the global scope (window) like this:
window.myReactComponents = {
myFirstComponent: () => <MyFirstComponent/>
}
Once this is bundled reference the bundlejs file on your non-react page.
In your non react page in the global scope reference ReactDOM.render with the root element which it should use to render like this:
window.useComponent = {
renderMyFirstComponent : ReactDOM.render(
window.myReactComponents.myFirstComponent(),
document.getElementById('myReactElement')
)
};
That's it!
Live example
ReactDOM.render Documentation
Hopefully this is a slam-dunk for someone out there...my essential problem is this: I've built up a very nice set of react components which i can render in my asp.net 4.5 mvc 6 application using react.js, flux, gulp, and browserify.
as long as i have it structured so that the react components have all the data they need everything is perfect. My issue now is that I would like to have an MVC view include the react stuff, and inject run-time properties into the top-level component as it is created. Since I'm brpowserify-ing all of my react code into a bundle, i just include the one script tag in my view:
<script src="/js/modules/AuthContainer.jsx"></script>
But whereas I would normally use JSX syntax to instantiate my component with props like this:
...the view in ASP.NET never gets translated to pure JS, so that fails.
I've also tried:
ReactDOM.render
(
React.createElement(AuthContainer, { successPath: '/home' }),
document.getElementById('reactRoot')
);
...from inside a script block in my view but i get:
Uncaught ReferenceError: AuthContainer is not defined
But i'm sure i'm exposing 'AuthContainer' via the browserify-ed bundle, so i don't understand why it's unable to resolve that component.
I know there's a React.NET way to do this, but i can't get that server-side rendering to work with my components because I'm using jQuery to fetch data in componentDidMount and the server-side rendering is choking looking for $() jQuery stuff.
I'd love to get the server side rendering going but right now i just need it to do work, one way of the other. Can someone provide a simple code snippet or gist of how to instantiate a React component from inside a cshtml file with run-time props?
One easy solution is this, just put your server side properties with Javascript in a global:
index.cshtml
<script>
var __config__ = {
base: "#MyBackEdnVariable",
initialCount: "#Count",
user: {
id: #user.id,
name: #user.name,
}
};
</script>
<script src="/js/modules/AuthContainer.jsx"></script>
And with React use that global variable:
AuthContainer.js
class AuthContainer extends React.Component {
render() {
return (
<div>{this.props.user.name}</div>
);
}
}
AuthContainer.defaultProps = {
initialCount: __config__.initialCount,
user: __config__.user
};
For posterity:
ReactDOM.render
(
React.createElement
(
MyComponent,
{
prop1: #numericValue,
prop2: '#textValue',
}
),
document.getElementById('reactRoot')
);
the magic was the jsx-alternative syntax, which i was aware of couldn't get a handle on that day. This allows you to instantiate react using pure JS and therefor just embed inside a simple script tag in your cshtml.
hth.