Categories
discuss

TS2339: Property ‘style’ does not exist on type ‘Element’

Here’s the code:

const test = Array.from(document.getElementsByClassName('mat-form-field-infix'));
test.forEach((element) => {
    element.outerHTML = '<div class="good-day-today" style="width: 0px;"></div>'; // Please note that this line works fine!
    element.style.padding = '10px';
    element.style.borderTop = '0';
});

Error I get when compiled:

ERROR in src/app//.component.ts(101,21): error TS2339: Property ‘style’ does not exist on type ‘Element’. src/app//.component.ts(102,21): error TS2339: Property ‘style’ does not exist on type ‘Element’.

How can I fix it?

I tried to remove the Array.from... part, tried to use for of and for in, tried as any, but above is the way I have to do it.

Answer

You need a typecast:

Array.from(document.getElementsByClassName('mat-form-field-infix') as HTMLCollectionOf<HTMLElement>)

That’s because getElementsByClassName only returns HTMLCollection<Element>, and Element does not have a styleproperty. The HTMLElement however does implement it via it’s ElementCSSInlineStyle extended interface.

Note that this typecast is typesafe in the way that every Elementis either a HTMLElement or an SVGElement, and I hope that your SVG Elements don’t have a class.

Categories
discuss

Kotlin Flow vs Android LiveData

I have some questions about Kotlin Flow

  1. I can observe LiveData from multiple Fragments. Can I do this with Flow? If yes then how?
  2. We can have multiple LiveData from a single LiveData using map& switchMap. Is there any way to have multiple Flow from a single source Flow?
  3. Using MutableLiveData I can update data from anywhere using the variable reference. Is there any way to do the same with Flow?

I have a use-case like: I will observe a SharedPreferences using callbackFlow{...} which will give me a single source Flow. From that Flow, I want to create multiple Flow for each key-value pair.

These might sound silly questions. I am new to Rx and Flow world.

Answer

I can observe LiveData from multiple Fragments. Can I do this with Flow? If yes then how?

Yes. You can do this with emit and collect. Think emit is similar to live data postValue and collect is similar to observe. Lets give an example.

Repository

// I just faked the weather forecast
val weatherForecast = listOf("10", "12", "9")

// This function returns flow of forecast data
// Whenever the data is fetched, it is emitted so that
// collector can collect (if there is any)
fun getWeatherForecastEveryTwoSeconds(): Flow<String> = flow { 
    for (i in weatherForecast) {
        delay(2000)
        emit(i)
    }
}

ViewModel

fun getWeatherForecast(): Flow<String> {
    return forecastRepository.getWeatherForecastEveryTwoSeconds()
}

Fragment

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    // Collect is suspend function. So you have to call it from a 
    // coroutine scope. You can create a new coroutine or just use 
    // lifecycleScope
    // https://developer.android.com/topic/libraries/architecture/coroutines
    lifecycleScope.launch {
            viewModel.getWeatherForecast().collect {
                    // Use the weather forecast data
                    // This will be called 3 times since we have 3 
                    // weather forecast data
            }
    }
}

We can have multiple LiveData from a single LiveData using map& switchMap. Is there any way to have multiple Flow from a single source Flow?

Flow is very handy. You can just create flow inside flow. Lets say you want to append degree sign to each of the weather forecast data.

ViewModel

fun getWeatherForecast(): Flow<String> {
    return flow {
        forecastRepository
            .getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
                .map {
                    it + " °C"
                }
                .collect {
                    // This will send "10 °C", "12 °C" and "9 °C" respectively
                    emit(it) 
                }
    }
}

Then collect the data in Fragment same as #1. Here what happens is view model is collecting data from repository and fragment is collecting data from view model.

Using MutableLiveData I can update data from anywhere using the variable reference. Is there any way to do the same with Flow?

You cant emit value outside of flow. The code block inside flow is only executed when there is any collector. But you can convert flow to live data by using asLiveData extension from LiveData.

ViewModel

fun getWeatherForecast(): LiveData<String> {
    return forecastRepository
    .getWeatherForecastEveryTwoSeconds()
    .asLiveData() // Convert flow to live data
}

In your case you can do this

private fun getSharedPrefFlow() = callbackFlow {
    val sharedPref = context?.getSharedPreferences("SHARED_PREF_NAME", MODE_PRIVATE)
    sharedPref?.all?.forEach {
        offer(it)
    }
}

getSharedPrefFlow().collect {
    val key = it.key
    val value = it.value
}

Edit

Thanks to @mark for his comment. Creating a new flow in the view model for getWeatherForecast function is actually unnecessary. It could be re-written as

fun getWeatherForecast(): Flow<String> {
        return forecastRepository
                .getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
                    .map {
                        it + " °C"
                    }
    }

Categories
discuss

React | props inside UseCallBack not updating inside custom hook

I’ve build an custom custom post hook that returns the API response and the API post. And I am using useCallback hook to set the Response state

Where it goes wrong is that the Package prop doesn’t update inside the useCallback hook.

When I log Package outside the useCallback hook I get the right data inside the propertie. However when I log the Package prop inside the useCallback hook the value of Package doesn’t change.

No matter how many times I press the button

I’ve tried creating an order state that updates everytime the Package prop updates, however whenever I set Package as an value in the scope I get an infinite loop.

I’ve alos added Package into the scope of the useCallback hook

example

  React.useEffect(() => {
    setOrder(Package);
  }, [Package]);

What I expect to happen is that whenever I call my custom usePostOrder hook the value of Package that is inside the useCallback is always up to date with the latest passed on prop.

CustomHook

/**
 * custom post hook that returns the API response and the API post function
 * @param {string} url
 * @param {object} Package
 * @returns {array} and @param {function}
 */

export const usePostOrder = (url, Package) => {
  const [loading, setLoading] = React.useState(true);
  const [order, setOrder] = React.useState(Package);
  const [response, setResponse] = React.useState({
    config: {
      data: []
    },
    data: {
      id: 0
    }
  });

  console.log("outside func", Package);
  const postOrder = React.useCallback(async () => {
    console.log("inside func", Package);
  }, [url, loading, Package]);

  return [response, postOrder];
};

Answer by Jake Luby with a slight adjustment

/**
 * custom post hook that returns the API response and the API post function
 * @param {string} url
 * @param {object} Package
 * @returns {array} and @param {function}
 */

export const usePostOrder = (url, Package, send) => {
  const [postOrder, setPostOrder] = React.useState();
  const [response, setResponse] = React.useState({
    config: {
      data: []
    },
    data: {
      id: 0
    }
  });

  React.useEffect(() => {
    const getData = async send => {
      //this will have the updated input Package
      await axios
        .post(ApiUrl + url, Package)
        .then(function(response) {
          setResponse(response);
        })
        .catch(function(error) {
          setResponse(error);
          console.log(error);
        });
    };

    send && getData();
  }, [send]); //this will run when url or Package changes

  return [response, postOrder];
};

useAsyncEndpoint.PropTypes = {
  url: PropTypes.url,
  user: PropTypes.object,
  club: PropTypes.object,
  cartItems: PropTypes.array
};

How I call this hook

import {usePostOrder} from "./yourHooksFolder"
  const [send, setSend] = React.useState(false);
  const [response, postOrder] = usePostOrder(
    "url",
    createOrder(user, store, cartItems),
    send
  );

  React.useEffect(() => {
    setSend(false);
  }, [response]);

// send order
  const onGoToPaymentPressed = () => {
    setSend(true);
  };



Answer

useCallback is not meant to be used like that. It doesn’t actually run the function, it simply memoizes it so that between renders the same function isn’t recreated.

What you want is the useEffect hook and to have the postOrder as part of the state:

export const usePostOrder = (url, Package) => {
  const [postOrder, setPostOrder] = React.useState()
  const [response, setResponse] = React.useState({
    config: {
      data: []
    },
    data: {
      id: 0
    }
  })


  React.useEffect(() => {
    const getData = async url => {
        //this will have the updated input Package
        console.log(Package) 

        //here is where you'll have your REST calls

        //set your data which will then update the return values in the hook and cause a rerender
        setPostOrder(returnValue)
        setResponse(someResponse)
    }

    getData()
  }, [url, Package]) //this will run when url or Package changes

  return [response, postOrder]
}
Categories
discuss

How to enable async/await with babel with support for IE11

I am hoping to use async/await in my source code and have it transpiled by babel to something useable by >0.25% not dead.

My head is spinning with the plethora of ways to attack this. Some are deprecated, some flat out don’t work, and the one that I have gotten to work more than doubles the size of my library.

I’ve tried using @babel/polyfill with @babel/plugin-transform-async-to-generator and it works well, but the library goes from ~500kB to ~1.1MB.

I also tried leaving it to @babel/preset-env by giving it >0.25% not dead, but that hasn’t made a difference. I get:

regeneratorRuntime is undefined

I’m hoping there is a better way to do this than including all this regeneratorRuntime stuff…

I’d rather go back to the callback pyramid of doom than ship a library over 1mb…

I am using:

  • webpack 4.41.0
  • babel 7.6.2

Answer

If you only need the generator polyfill — which is needed for async/await — then you can just use facebook/regenerator.

You could follow the steps to support async/await in IE 11:

  • use babel-preset-env
  • yarn add regenerator or npm install regenerator
  • add node_modules/regenerator-runtime/runtime.js (10.7kb minified) into your bundle

Reference link: Add ES7 Async/Await Support for your Webapp in 3 Easy Steps

Categories
discuss

Redux vs custom hook

I learned react and Redux at the same time and went “all in” on Redux; basically all state is stored in Redux. And I followed the standard allIds, byId state shape pattern as detailed here.

My app is very data-centric, it talks to an API, and does alot of CRUD type actions – fetchAll, fetchById, add, update, delete.

The API communication is segregated into a “service layer” module that is its own npm package. All calls to this service layer are in the Redux actions, using redux-thunk.

I’ve realized there is no need to put most everything in Redux, the data is really needed on a specific component, for example. And I would love to simplify this architecture.

So I began to refactor into a custom hook instead. It seemed since my state shape was more of an object rather than scalar, I should use useReducer rather than useState

// reducer
// ------------------------- 
const initialState = {
  adding: false,
  updating: false,
  deleting: false,
  error: null,
  items: null
};
const reducer = (state, action) => {
// implementation omitted for brevity. . .
}
const useItemsApi = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  // wrapped in useCallback because called in component's useEffect
  const fetchItems = useCallback(async (options) => {
    try {
      const resp = apiService.fetchItems(options);
    } catch (err) {
      if(err.status === 401) 
         // send to login screen
      else
         dispatch({type: 'error', payload: err});
    }
  }, [options]);

  // addItem, updateItem, deleteItem, etc...

  const actions = {fetchItems, updateItem, addItem, deleteItem};
  return [state, actions];
};

// component
// ------------------------- 
const component = (props) => {
  const [state, actions] = useItemsApi();
  const {fetchItems, updateItem, addItem, deleteItem} = actions;
  useEffect(() => {
     fetchItems()
  }, fetchItems);

  // omitted for brevity...
}

When I got to setting the state in the reducer for the update action, I realized it would be easier if I used “allIds” and “byId” pattern.

And at this point I thought – how is this any different than using Redux?

It is going to end up looking like almost the exact same code, and I’m losing some power of selectors, but removing the complexity of redux-thunks. And my current redux actions include specific use case actions (special save for item type X, for ex.) so I’d need to find a place for those.

My question is – is there any reason to refactor this to a hook using local state?

Answer

Advantages of storing the state in Redux:

  • You can access and modify it globally
  • It persists even after your component is unmounted

Advantages of storing the state in the component:

  • You can have multiple components with different values in the state, which may be something you want
  • …Or you could even have multiple hooks of the same type in one component!
  • You don’t need to switch between files. Depending on how your code is organized, Redux can be split into 3 files + 1 file for the component which uses it – while this can help keep your code well-structured for complex use cases, it can be an overkill for keeping track of a simple state. Having to switch between multiple files to work on one component can reduce your productivity (I don’t like having to keep track of 4 tabs in my IDE for every feature I work on).
  • (Also, hooks are new and cool.)

So, use Redux if:

  • You need to share state between multiple components (or plan to in the future)
  • You need to keep state even when the component that uses it is unmounted

You might prefer to keep the state in React (hooks or otherwise) in other cases since they simplify your code a bit.

But that doesn’t mean you need to refactor your entire codebase. If you think your code is concise enough and you like the way it is organized, or if you are unsure if you will need the state globally in the future, you can keep it in Redux – there is nothing wrong with that!

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..