Friday, October 15, 2021

Understanding React hooks: useState, useEffect

useState:

useState is for managing the component states. Note setState is not synchronous method, so the state value will not be updated immediately after the setState method returns. However, eventually the state will be updated in the following rendering.

Although useState is asynchronous, it will be executed under the same context when the method is initially called. If it is called in an event handler, then the async state updating will be executed from the event handler's callstack. If it is called from function component's main rendering function, then the async state updating will also happen in the main rendering function.    

If setState is called in the function component's main method, then it will cause runtime errors if the setState method internally calls other component's setState method, and triggers the updating of other components. It is a good practice to always call setState method in event handler, and also do not call other component's setState method within current component's setState method.

useEffect:

useEffect's main purpose is providing a place to execute some code outside of the function component's main render function. This will speed up and simplify the logic in the main render function, in addition, certain functions are not allowed to be called from the function component's main render function, for example, calling other component's setState method within the main render function is not allowed. So useEffect is a good place to execute the code after the rendering is finished. 

useEffect can be configured by the second parameter to decide when its logic will be called, it can be called after every rendering (without providing second parameter), only after first rendering (providing an empty array in the second parameter), or whenever certain states or variables have been change (including the states or variables in the second array parameter).

When an event handler is included in the array of the second parameter of useEffect, whenever a new event handler is created during executing the component render function, the function of useEffect will always be called because the event handler has been updated. To avoid executing the body function of useEffect triggered by the new event handler, useCallback hook can be used to always return the same event handler object during different rendering of the same component. 

The function passed to useEffect may return a clean up function. The returned function will be called automatically when the component is removed from UI, or when next time the effect body function is called again.

Saturday, October 2, 2021

Apply CSS style to react material ui component

There are few ways to customize css style for native html elements (like button, div, etc) and React Material UI components (like Button, TextField, etc). Note when styles are specified as javascript or typescript object in Material UI syntac, the style attribute name is different from html css style attribute name, please refer to below link for more details.

https://mui.com/system/properties/

 

1. For Material UI components, if the css style applies to global scope, the best way to set it is in custom theme file as shown below:

const myTheme = createTheme({
    components: {
      MuiButton: {
        defaultProps:{
          disableElevation: true,
        },
        styleOverrides: {
          root: {
            borderRadius: '2em',
            width: '9em',
            textTransform: 'none',
            border: '1px solid #0076bf',
            color: '#fff',
            backgroundColor: '#0076bf',
            "&:hover" :{
              color: '#0076bf',
              backgroundColor: '#fff',
              borderr: '1px solid #0076bf',
            }
          }
        }
      }
    }
  });

  export default myTheme;

This method requires using Material UI style attribute name instead of html css style attribute name. For example, to change the background color, 'background-color' does not work, only backgroundColor will work. (The test shows bgcolor also does not work when specified in theme file, only backgroundColor works here).

If you need to customize native html element style in global css scope, then you can just use the old way to set it in a regular html css file.

2. When applying css style as named css class to certain elements including both Material UI component and native html elements.

This is a simple way to reuse the css style defined for regular html and css web projects. The style must be defined with specific css class name in a regular css file. Note in this way, you define css styles using regular html css attribute name and syntax, not Material UI's attribute names. Then in js (or jsx) file, importing the css file and apply the class styles to the corresponding elements.

The element must specify the class attribute name as 'className' instead of the regular 'class'. The reason of changing the name of 'class' to "className" is, js or jsx file is not regular html file, those jsx elements are not real html elements, and react needs to translate these jsx attributes to the regular html element attributes when running the function component method, where className will be translated to html 'class' attribute.  The below code applies row and mb-2 class style to div element

<div className="row mb-2">

As a react expression, className can also take react expression as value. React expression is wrapped by {}, so react expresion of {"row mb-2"} will just return the string of value of "row mb-2". So it has the same effect as 
<div className={"row mb-2"}

More important, when using react expression as class value, back tick can be used to dynamically set a sub react expression as class name, the sub react expression must be wrapped with ${}. For example:
<div className={`row mb-2 ${!isValid? 'invalidClassName' : 'validClassName'}`}

The css style defined in css file applies to global scope, so any elements having those style classe names will have those styles classes applied to them.
 
3. If you only want to apply a particular css style to a certain elements (native html elements or Material UI components), then you can apply the them as inline css style. This method specifies the style as React object, so the style attribute name must use the name defined by Material UI, not the html css style attribute name. 

The style attribute's value is a react expression (wrapped by {}) of a javascript object (wrapped by another {}), so the value is wrapped in {{}}.

<label style={{backgroundColor:'red', width:'100px'}}>hello</label>

The limitation of this method is it is hard to set complex styles, for example, no easy way to setup hover state styles for the elements.

4. For material ui components (not native html element), it can also set sx (system) attribute to define custom style based on theme. Note the style name used for sx needs to use material ui's style attribute name. The style will only apply to the current Material ui component.
<Stepper activeStep={activeStep} sx={{ pb: 3 , bgcolor: 'text.secondary'}}>
<Button sx={{
                                      color:'#0076bf',
                                      backgroundColor: '#fff',
                                      width: '9em',
                                      border: '0px',
                                      cursor: 'pointer',
                                      '&:hover':{
                                        textDecoration: 'underline',
                                      }
                                }} onClick={props.onPrev}>{t("button.previous")}</Button>
Please refer to https://mui.com/pt/system/the-sx-prop/ for available style names to be used by sx.