How can I change an image dynamically in Ionic React?

1.2k Views Asked by At

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;
1

There are 1 best solutions below

9
On BEST ANSWER

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