1

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;
traxx2012
  • 394
  • 2
  • 16
  • Can I ask why is this tagged `react-native` while dealing with html elements? Either way, even if you wish to do this in react-native, implementation should be fairly similar, but in case it is, please create a separate question for that instead. I removed the tag for now. – Samuel Hulla Oct 16 '20 at 18:30

1 Answers1

2

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

Guruparan Giritharan
  • 15,660
  • 4
  • 27
  • 50
  • 1
    I disagree about the final part of your answer, while redux has its usefulness in certain cases, this one isn't one of them and installing a new package for what can be elegantly handled by react's context is completely unnecessary. Either way you got a +1 from me. – Samuel Hulla Oct 16 '20 at 18:23
  • Well, thats true but if he is already using redux he can use it, I'm coming from react-native world and persisting redux state is an option i had so something like theme it would be useful, thanks a lot for the upvote :) – Guruparan Giritharan Oct 16 '20 at 18:26
  • 1
    First of all, thank you VERY much for this elaborate answer! I greatly appreciate explanations like this so I can learn. I'm on the road atm so I will your code against my project later and if everything works, I will mark your answer of course :) – traxx2012 Oct 16 '20 at 18:42
  • So, I've now come back home. First, I got stuck with the code you provided for the app.js/tsx file. I've added mine to my original post, could you point out how to integrate your snippet? – traxx2012 Oct 16 '20 at 18:47
  • you can have a state in the app.js like my sample and have the Context.Provider to wrap the other components – Guruparan Giritharan Oct 16 '20 at 18:48
  • you will have to import it in all components, i've made it as a separate file in the sandbox – Guruparan Giritharan Oct 16 '20 at 19:05
  • I managed everything to this point but somehow I always get `Property 'setTheme' does not exist on type '{ theme: string }'.` I have imported useContext, useState and the AppContext in this component and everything else doesn't throw any errors... I'm sorry to bother you like that. – traxx2012 Oct 16 '20 at 19:30
  • No problem at all, maybe provide a default value for setTheme in AppContext file, even though it's useless it might hide the error. – Guruparan Giritharan Oct 17 '20 at 00:12
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/223192/discussion-between-guruparan-giritharan-and-traxx2012). – Guruparan Giritharan Oct 17 '20 at 10:24