2

I have a state variable initialized like this:

    const App = () => { 
        const [parameters, setParameters] = useState({
            "shape": "circle",
            "fontFamily": "",
            "size": 500
        });

        //...

        // which is here passed to a component
        return(
            <MyCanvas props={parameters} />
        );
    }

The parameters array is used by another component that renders something on the canvas. I want that the parameters is on first load of the app, at runtime, to be what's currently defined in CSS, so I want to read that CSS property and init, in this case, the "fontFamily" field with what's written in CSS. Therefore want to read the @font-face {font-family: MyLocalFont;} property from the CSS definition.

So generally: How would I read a value during runtime from a CSS for initializing the useState variable?

A compile-time solution would not meet my needs. Changes to the CSS styles can be made without re-deploying my widget, therefore I need to read the actual CSS property values during runtime upon initialization.

user6329530
  • 596
  • 1
  • 7
  • 21
  • The statement and the question are confusing, can you frame the question correctly? OR Elaborate it more... – champion-runner Mar 25 '21 at 11:14
  • What exactly is confusing? – user6329530 Mar 25 '21 at 11:15
  • @Martin would this run in a compiled app, when the css is changed and not recompiled? Say, is it possible without having scss compiled? – user6329530 Mar 25 '21 at 11:28
  • No and yes. That's a compile time feature. But it works via the css-loader, not the sass-loader, so you can change the css and it would work as long as the css is your input to your loader. Not exactly sure what your scenario is. – Martin Mar 25 '21 at 11:30
  • Why did you close the question? I did not ask about SCSS I ask about CSS – user6329530 Mar 25 '21 at 11:31
  • Can you clarify if you need to get this value during rundtime or during build time? Maybe this will make a difference to decide whether it is a duplicate question. – Martin Mar 25 '21 at 11:35
  • When you want to read it during runtime you must get a ref to your app root element (use a callback ref) and call [getComputedStyle](https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle) and then you read the font-family value from the computed style. – Martin Mar 25 '21 at 11:41
  • @Martin Yes, I wanted to read it during run-time. PS: Note the last line of my question, I explicitely asked about how to read a value from a css file to initialize a useState variable with that value. – user6329530 Mar 25 '21 at 11:42
  • @Martin Ok, so please reopen the question, SCSS doesn't help me here. – user6329530 Mar 25 '21 at 11:43
  • 2
    Check out this answer: https://stackoverflow.com/a/26272668/2570353. – sergdenisov Mar 25 '21 at 11:44
  • I can vote to re-open and I will do that, but when you write "read the value from css file" everybody who reads it will think "during build time". For me it is not clear or obvious that your question asks for "access css property value during runtime from javascriptcode" (and on a sidenote: maybe that will actually be a better title). And I'm curious why you need it during runtime, but that's just me. – Martin Mar 25 '21 at 11:47
  • I can clarify the question regarding run time. The app supposed to be embedded into an existing CMS to manage a canvas displayed on the site. CSS may change in the CMS so I wanted to keep track of that. – user6329530 Mar 25 '21 at 11:52
  • @sergdenisov I am currently trying to check if that hint could help me here. – user6329530 Mar 25 '21 at 11:53
  • @Martin Edited the question and made it more general to indicate accessing CSS properties at run time, not depend on a specific file (like App.css, which deceptively might suggest a compile time issue). – user6329530 Mar 25 '21 at 12:01
  • 1
    I voted to re-open because the linked answer only covers the build-time feature – Martin Mar 25 '21 at 12:01
  • Do you know which element this `@font-face {font-family: MyLocalFont;}` gets applied to? Maybe the ``? Because if so you could retrieve it with `getComputedStyle(document.body).getPropertyValue('font-family')` – Martin Mar 25 '21 at 13:52
  • @Martin I had to add it to a hidden element, because the font needs to be loaded by the browser and applied to an element, just declaring it in the CSS was not enough. Called it ".hiddenfont" and made a span with a nbsp for it. So I can use your solution (or what sergdenisov suggested) and either select on that hiddenfont class or reference that span element like you did with the div in your answer. But I think with ref is better, because that way it makes sure, useCallback runs once the element is ready in the DOM, right? – user6329530 Mar 25 '21 at 18:44
  • Yes the callback ref is the right choice if you need to get notified when the element was finally added to the DOM. (Caution: Not sure if you need to null-check the `element` argument) – Martin Mar 26 '21 at 07:45
  • @Martin I just tested, null-check is not needed, thanks to ref useCallback with empty dep array seems to run exactly once after `element` is ready in the DOM, console.log(element) prints out the full span element once at loading of the app. – user6329530 Mar 26 '21 at 11:24

2 Answers2

2
import React, { useCallback } from 'react';

const defaultParameters = {
  "shape": "circle",
  "fontFamily": "",
  "size": 500,
}

const App = () => { 
  const [parameters, setParameters] = useState(null);

  const appRoot = useCallback(
    (element) => {
      const style = window.getComputedStyle(element);
      const fontFamily = style.getPropertyValue('font-family');
      setParameters({...defaultParameters, fontFamily });
    }, []
  );

  //...

  return (
    <div ref={appRoot}>
      { parameters && <MyCanvas props={parameters} /> }
    </div>
  );
}
Martin
  • 5,714
  • 2
  • 21
  • 41
-1

You can use the 'classNames' package.

https://www.npmjs.com/package/classnames

npm install --save classnames

import classNames from 'classnames';

const Testers = () => {
  const [parameters, setParameters] = React.useState({
    shape: 'circle',
    fontFamily: '',
    size: 500,
  });
//I think you can change the font name when you need it.
  React.useEffect(() => {
    setParameters({ ...parameters, fontFamily: 'NotoSans' });
  }, []);

  return (
       <div className={classNames(parameters></div>

     ....

Also, it would be better to designate a separate font as below method.

  const [fonts, setFonts] = React.useState('fontName')
  const [parameters, setParameters] = React.useState({
    shape: 'circle',
    size: 500,
  });

  React.useEffect(() => {
    setFonts('NotoSans');
  }, []);


    <div className="header">
      <div className={classNames(parameters, {
        fontFamily : fonts
      })}></div>


imer
  • 55
  • 5