Reputation: 420
I'm pretty new to React and TypeScript, and I ran into this problem:
In my UI I'm using several decorative graphics as follows:
import Kitten from './img/Kitten.png';
<img className="Image" src={Kitten} />
Now, I have a dark-mode toggle. When it fires, I want to replace all images with their appropriate dark-mode version. I was thinking about something like this:
import Kitten from './img/Kitten.png';
import DarkKitten from './img/DarkKitten.png';
//gets called when dark mode is toggled on or off
const darkModeToggleFunc = () => {
document.querySelectorAll('.Image').forEach(element => {
if(element.src.includes("Dark")) {
element.src = element.src.replace("Dark", "");
} else{
element.src = "Dark" + element.src;
}
});
}
<img className="Image" src={Kitten} />
Now, in React I have two problems: the .src-attribute is unknown because element
is not necessarily an image and the second problem is: I don't assign URIs as src but the variable from the import. So there isn't really a string I can change... If I'm informed correctly, React uses Base64 for images specified this way.
How could I achieve my goal in React?
Edit: App.tsx
//bunch of imports
const App: React.FC = () => {
return (
<IonApp>
<IonReactRouter>
<IonSplitPane contentId="main">
<Menu />
<IonRouterOutlet id="main">
<Route path="/page/:name" component={Page} exact />
<Redirect from="/" to="/page/Production" exact />
</IonRouterOutlet>
</IonSplitPane>
</IonReactRouter>
</IonApp>
);
};
export default App;
Upvotes: 1
Views: 1318
Reputation: 16334
First things first when it comes to react you dont directly go and change things in the document level, you update the virtual DOM and let react take care of the rest.
You scenario is on changing the theme of the app, this answer is on using React context to change theme and use images appropriately.
First you create a Context which will hold the theme value
const AppContext = createContext({
theme: "light",
setTheme: (theme) => {}
});
Here we are going to use a state variable for simplicity, you can use anything you prefer. Heres the app.js file
export default function App() {
const [theme, setTheme] = React.useState("light");
const themeState = { theme, setTheme };
return (
<AppContext.Provider value={themeState}>
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<ImageViewer />
<DarkModeSwitch />
</div>
</AppContext.Provider>
);
}
Here we set the theme value in the state and set the context to that, the setTheme can be used to update the theme from any component that is in the tree. in your case the darkmodeswitch, here we toggle the value
const DarkModeSwitch = () => {
const { theme, setTheme } = useContext(AppContext);
const darkModeToggle = () => {
setTheme(theme === "light" ? "dark" : "light");
};
return (
<div>
<input
type="checkbox"
checked={theme === "light"}
onChange={() => darkModeToggle()}
/>
</div>
);
};
Coming to your main requirement, the images, lets use a common files for images with the contents
export const Kitten ="image source 1";
export const KittenDark ="image source 2";
You simply set the image based on the theme like below
import { Kitten, KittenDark } from "./images";
export default function ImageViewer() {
const { theme } = useContext(AppContext);
return (
<img
alt="kitten"
style={{ height: 50, width: 100 }}
src={theme === "light" ? Kitten : KittenDark}
/>
);
}
as you can see everything is connected via the context and once you update the context you can see the images change. You can see a working version here https://codesandbox.io/s/react-theme-switch-3hvbg
This is not 'THE' way, this is one way of handling the requirement, you can use things like redux etc
Upvotes: 2