I am using React and Sass on my project and a library called ParticleJs as background and I'm hoping to achieve dark mode feature on my project where when a user wants it, the data in ParticleJS json file is changed.
Here's a snippet of the json file I'd like to change conditionally specifically, bg.color.value
"background": {
"color": {
"value": "#edf2f8"
},
"position": "50% 50%",
"repeat": "no-repeat",
"size": "20%"
},
Here is the whole ParticleJS.jsx file:
import Particles from "react-tsparticles";
import { loadFull } from "tsparticles";
import particlesOptions from "./particles.json";
import React, { useCallback } from "react";
const ParticleBackground = () => {
const particlesInit = useCallback((main) => {
loadFull(main);
}, []);
const particlesLoaded = (container) => {
console.log(container);
};
return (
<Particles
init={particlesInit}
loaded={particlesLoaded}
options={particlesOptions}
/>
);
};
export default ParticleBackground;
Is there any other way that I can do it than creating another js folder (to maybe call two ParticleJS components conditionally) or maybe create another json file?
Thank you in advance
According to the docs you don't need to pass JSON into the options prop you can just pass a javascript opject. If you include a "useState" variable in the object for the bg color (for example) you can update it as you need.
import Particles from "react-tsparticles";
import { loadFull } from "tsparticles";
import React, { useCallback, useState} from "react";
const ParticleBackground = () => {
const particlesInit = useCallback((main) => {
loadFull(main);
}, []);
const particlesLoaded = (container) => {
console.log(container);
};
const [bgColor, setBgColor] = useState('#edf2f8');
//call setBgColor when you want to update the bg
return (
<Particles
init={particlesInit}
loaded={particlesLoaded}
options={{
background: {
color: {
value: bgColor
},
position: "50% 50%",
repeat: "no-repeat",
size: "20%"
}
}}
/>
);
};
export default ParticleBackground;
Sure - particlesOptions is imported as a plain Javascript object, so you can write a function to modify (a copy of) it, e. g.
function getParticleOptions(x) {
// Shallow copy of the original object
const options = {...particlesOptions};
options.x = x; // or whatever
return options;
}
Then pass the returned value to the particle component.
Related
While using mui react, I want to color some non material ui custom components made with div using colors from my theme's primary palette.
I currently can use theme.palette.primary.main or the .light colors directly, including the theme.palette.text.primary for texts colors.
But if I change the theme to dark mode, I will have to change the color reference as well, by checking on theme.mode with conditionals like the following:
<div
style={{
backgroundColor:
theme.palette.mode == "light"
? theme.palette.primary.dark
: theme.palette.primary.light
}}
>
Test
</div>
So is there a way we can make this work just like in the material ui components? Passing theme.palette.primary will work with theme mode changes?
Maybe something simpler like:
<div style={{ backgroundColor: theme.palette.primary }}></div>
You could use Context to save your theme setting globally and also you need to separate the themes like light and dark.
// SettingsContext.js
import React, {
createContext,
useState
} from 'react';
const defaultSettings = {
// you could add sort of global settings here
theme: 'light'
};
const SettingsContext = createContext({
settings: defaultSettings,
saveSettings: () => {}
});
export const SettingsProvider = ({ settings, children }) => {
const [currentSettings, setCurrentSettings] = useState(settings || defaultSettings);
return (
<SettingsContext.Provider
value={{
settings: currentSettings,
}}
>
{children}
</SettingsContext.Provider>
);
};
export const SettingsConsumer = SettingsContext.Consumer;
export default SettingsContext;
You can build the settings context as a hook but you can skip this.
// useSettings.js
import { useContext } from 'react';
import SettingsContext from './SettingsContext';
const useSettings = () => useContext(SettingsContext);
export default useSettings;
And then try to create your custom theme which includes the dark and light mode.
// theme.js
import { createTheme as createMuiTheme } from '#mui/material/styles';
const themes = {
'light': {
...
},
'dark': {
...
}
];
export const createTheme = (name) => {
return createMuiTheme(themes[name]);
}
After that, you need to pass the theme which you have selected in App.js or index.js whatever top level file.
// App.js
...
import { ThemeProvider } from '#mui/material/styles';
import useSettings from './useSettings';
import { createTheme } from './theme.js';
const App = () => {
const { settings } = useSettings();
const theme = createTheme(settings.theme);
return (
<ThemeProvider theme={theme}>
...
</ThemeProvider>
);
};
export default App;
...
That's all.
Now you can use the selected theme without conditional render in your components.
<div style={{ backgroundColor: theme.palette.primary }}></div>
But this will not prevent the selected theme after hard refresh.
So if you want to keep the theme as selected even after refresh your browser, then you could save the theme setting in the localStorage.
I need to translate my app, but i don't knomw how to use useTranslation() in the top-level files (i store there some consts which contain some text). One of this file is
import { useTranslation } from "react-i18next";
const {t} = useTranslation()
export const selectThemeOptions = [
{ value: "choose", text: "Choose theme" },
{ value: "Algebra", text: "Algebra" },
{ value: "Geometry", text: "Geomerty" },
{ value: "Programming", text: "Programming" },
{ value: "Logic", text: "Logic" },
];
so in this case i have an error:
src\Configs\ThemesOfProblems.js
Line 3:13: React Hook "useTranslation" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks
I need this array in my component, and it use in the next fragment :
<Form.Group as={Col} controlId="problemTheme">
<Form.Label>{t("userprofile.theme")}</Form.Label>
<Form.Select
name="theme"
value={values.theme}
onChange={handleChange}
isValid={touched.theme && !errors.theme}
isInvalid={!!errors.theme}
onBlur={handleBlur}
>
{selectThemeOptions.map((el, index) => {
return <option key={index} value={el.value}> {el.text} </option>
})}
</Form.Select>
</Form.Group>
And i've got a lot of such situations, i don't have any ideas how to do it
Basically it says it has to be called in a react component. It could be called in a functional component where you return your jsx or a class component that has a render method in it. If you call the function outside of one of these, then you will get this error.
You called const {t} = useTranslation(); outside of a React component, your selectThemeOptions file seems to be regular JavaScript due to the absence of JSX or a returning statement with your HTML.
Here is the correct way to do it:
/* Everything above this point is considered top-level, hence using your `useTranslation()` hook here would cause an error */
const Component = (props) => {
const { t } = useTranslation();
}
export default Component;
Here is a way to organise your translations:
• Your src folder should contain an i18n.js file with the following code:
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import en from "../locales/en.json";
import ru from "../locales/ru.json";
const isReturningUser = "lang" in localStorage; // Returns true if user already used the website.
const savedLanguage = JSON.parse(localStorage.getItem("lang")); // Gets persisted language from previous visit.
// Get previously used language in case of returning user or set language to Russian by default.
const language = isReturningUser ? savedLanguage : "ru";
const resources = {
en: {
translation: en,
},
ru: {
translation: ru,
},
};
i18n.use(initReactI18next).init({
resources,
lng: language,
keyseparator: false,
interpolation: {
escapeValue: false,
},
});
export default i18n;
Your src folder should contain a locales folder with json files of the languages your application uses. Example: ru.json and en.json:
{
"choose": "Выбрать",
"chooseATheme": "Выбрать тему",
"algebra": "Алгебра",
"geometry": "Геометрия",
"programming": "Программирование",
"logic": "Логика",
}
Your component should look like this – note that the translations are in json files instead of your React component – :
import React from "react";
import { useTranslation } from "react-i18next";
const Component = (props) => {
const { t } = useTranslation();
const selectThemeOptions = [
{ value: t("choose"), text: t("chooseATheme") },
{ value: t("algebra"), text: t("algebra") },
{ value: t("geometry"), text: t("geometry") },
{ value: t("programming"), text: t("programming") },
{ value: t("logic"), text: t("logic") },
];
return( //Your UI )
}
export default Component;
This way, your translations wouldn't be hard-coded on your selectThemeOptions and will adapt to whichever translation your json locales contain.
Please tell me if this works.
Edit: If you want a concrete example of implementation of my solution here it is: https://github.com/YHADJRABIA/ecommerce/tree/main/src
Edit2: There might be a better solution of doing this, this is merely what worked for me.
Edit3: Following Nikita's comment, here's a solution to use the translation function outside of a react component —How to use react-i18next inside BASIC function (not component)?
P.S. Since you are from Belarus I assume that you want your translation to be made in Russian since Belarusian isn't as widely spoken.
I'm developing a Mobile Application using React Native. I came up with a thought of, is it possible to use multiple global states using the same Context.
So, I implemented a context like this.
// GlobalStateContext.js
import React, { createContext, useContext, useEffect } from 'react';
const GlobalStateContext = createContext({ globalStateValue: null, setGlobalStateValue: () => {} });
export function useGlobalState (initialValue = null) {
const { globalStateValue, setGlobalStateValue } = useContext(GlobalStateContext);
useEffect(() => { setGlobalStateValue(initialValue) }, []);
return [globalStateValue, setGlobalStateValue];
}
export default function GlobalStateContextProvider (props) {
const { value, children } = props;
return (
<GlobalStateContext.Provider value={value} >
{children}
</GlobalStateContext.Provider>
)
}
In my App.js, I've wrapped my App with the GlobalStateContextProvider.
// App.js
import GlobalStateContextProvider from './path/to/GlobalStateContext';
export default function App() {
const [globalStateValue, setGlobalStateValue] = useState(null);
return (
<GlobalStateContextProvider value={{ globalStateValue, setGlobalStateValue }} >
// <MyAppContent />
</GlobalStateContextProvider>
)
}
Then, I tried to do something like this on some of my screen files.
import { useGlobalState } from '../path/to/GlobalStateContext';
const [myState1, setMyState1] = useGlobalState('State 01');
const [myState2, setMyState2] = useGlobalState('State 02');
But, there are NO TWO SEPARATE global states. I know there should do something more in the implementation if it is possible to achieve what I want.
So, is this thing is possible? When using useState hook, we can do like,
const [myState1, setMyState1] = useState('State 01');
const [myState2, setMyState2] = useState('State 02');
In this case, we have TWO SEPARATE local states.
Like this, I want to know if it is possible to define some GLOBAL STATES and use them.
I'm trying to update various components in a dashboard with realtime data using a DataContext.
I'm using multiple states in the context's value property, one for each set of values i want to update together.
When setting one state however, all components which use any state from DataContext get re-rendered. Some values get updated multiple times per second, resulting in very poor performance.
Here is an example:
App.js
import React, { useState, useContext } from "react";
var DataContext = React.createContext();
function DataProvider({ children }) {
var [prop1State, setProp1State] = useState({test: 1});
var [prop2State, setProp2State] = useState({test: 2});
var [prop3State, setProp3State] = useState({test: 3});
setInterval(() => {
setProp2State(state => {
return {test: state.test + 1}
});
}, 1000);
setInterval(() => {
setProp3State(state => {
return {test: state.test + 1}
});
}, 2500);
return (
<DataContext.Provider
value={{
prop1: {
get: prop1State,
set: setProp1State
},
prop2: {
get: prop2State,
set: setProp2State
},
prop3: {
get: prop1State,
set: setProp1State
}
}}
>
{children}
</DataContext.Provider>
);
};
function SomeComponent(props) {
console.log("Rendering SomeComponent");
var dataContext = useContext(DataContext);
return (
<p>
{dataContext.prop1.get.test}
</p>
);
}
function App() {
return (
<DataProvider>
<SomeComponent/>
</DataProvider>
);
}
export default App;
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
The page just shows 1, but the console is full of "Rendering SomeComponent", triggered by the setInterval's that only update 2 and 3.
Can this be solved without wrapping SomeComponent in 3 different Contexts, one for each state?
Is having say 10 nested contexts bad?
Is there a better alternative to contexts in this case?
I've been having a lot of trouble implementing Dygraph in React (I'm using redux): http://dygraphs.com/. The Dygraph wrapper packages on NPM don't seem to work.
Also I can't simply use:
<div id="graph"></div>.
I believe this is because in react your working in a state instead of an actual index.html file.
So the method I'm currently trying to use is to create the graph component:
import React, { Component } from 'react';
import Dygraph from 'dygraphs';
import myData from '../../mockdata/sample-data.json';
import 'dygraphs/dist/dygraph.min.css'
import './graphComponent.css';
class DyGraph extends Component {
constructor(props) {
super(props);
// mock json data for graph
const messages = myData;
var data = "";
messages.forEach((response) => {
data += response[0] + ',' + response[1] + "\n";
});
new Dygraph('graphContainer', data, {
title: 'Pressure Transient(s)',
titleHeight: 32,
ylabel: 'Pressure (meters)',
xlabel: 'Time',
gridLineWidth: '0.1',
width: 700,
height: 300,
connectSeparatedPoints: true,
axes: { "x": { "axisLabelFontSize": 9 }, "y": { "axisLabelFontSize": 9 } },
labels: ['Date', 'Tampines Ave10 (Stn 40)'],
});
}
render() {
return <div></div>
}
}
export default DyGraph;
and then import it into:
import React, { Component } from 'react';
import DyGraph from './components/graph/graphComponent';
import './App.css';
class DeviceDetails extends Component {
render() {
return (
<div >
<DyGraph />
</div>
);
}
}
export default DeviceDetails;
And there is a Display state that if you click something it will go to:
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import WarningView from '../warning/warningView'
import DirectoryView from '../directory/directoryView'
import DeviceDetailView from '../devicedetails/devicedetails'
export const Display = ({ currentPage }) => {
switch(currentPage) {
case 'WARNING_PAGE':
return <WarningView/>;
case 'DIRECTORY_PAGE':
return <DirectoryView/>;
case 'SENSOR_PAGE':
return <DeviceDetailView/>;
default:
return <WarningView/>;
}
};
Display.propTypes = {
currentPage: PropTypes.string.isRequired,
};
export default connect(
(state) => ({ currentPage: state.currentPage }),
(dispatch) => ({ })
)(Display)
When I build and run this locally I get an error in the console (when I try to see the graph):
Uncaught (in promise) Error: Constructing dygraph with a non-existent div!
at Dygraph.__init__ (dygraph.js:217)
at new Dygraph (dygraph.js:162)
at new DyGraph (graphComponent.js:19)
at ReactCompositeComponent.js:295
at measureLifeCyclePerf (ReactCompositeComponent.js:75)
at ReactCompositeComponentWrapper._constructComponentWithoutOwner (ReactCompositeComponent.js:294)
at ReactCompositeComponentWrapper._constructComponent (ReactCompositeComponent.js:280)
at ReactCompositeComponentWrapper.mountComponent (ReactCompositeComponent.js:188)
at Object.mountComponent (ReactReconciler.js:46)
at ReactDOMComponent.mountChildren (ReactMultiChild.js:238)
If anyone can figure out what's going on or even give me a hint that'd be SOOO APPRECIATED. I specifically want to use dygraph instead of google charts or other react charts (which I've gotten working very easily), however, there is little information about dygraph implementation in React and I don't really understand why it won't work.
The problem is that this line:
new Dygraph('graphContainer', data, { ... })
Tries to create a Dygraph in the element with ID graphContainer. But there is no element with that ID, hence the failure.
You need to wait until React creates a div in the DOM to create the dygraph. You'll want to instantiate the Dygraph in componentDidMount:
class Dygraph extends Component {
render() {
return <div ref="chart"></div>;
}
componentDidMount() {
const messages = myData;
var data = "";
messages.forEach((response) => {
data += response[0] + ',' + response[1] + "\n";
});
new Dygraph(this.refs.chart, data, {
/* options */
});
}
}