โš›๏ธ useWorker() - A React Hook for Blocking-Free Background Tasks

Overview


useWorker

Use web workers with react hook
https://useworker.js.org/
Tweet

Bytes Newsletter

GitHub size GitHub TypeScript Support


๐ŸŽจ Features

  • Run expensive function without blocking UI (Show live gif)
  • Supports Promises pattern instead of event-messages
  • Size: less than 3KB!
  • Clear API using hook
  • Typescript support
  • Garbage collector web worker instance
  • Remote dependencies option
  • timeout option

๐Ÿ’พ Install

  • @latest
npm install --save @koale/useworker

๐Ÿ”จ Import

import { useWorker, WORKER_STATUS } from "@koale/useworker";

๐Ÿ“™ Documents


๐Ÿž Demo


โš™ Web Workers

Before you start using this hook, I suggest you to read the Web Worker documentation.


๐Ÿพ Usage

import React from "react";
import { useWorker } from "@koale/useworker";

const numbers = [...Array(5000000)].map(e => ~~(Math.random() * 1000000));
const sortNumbers = nums => nums.sort();

const Example = () => {
  const [sortWorker] = useWorker(sortNumbers);

  const runSort = async () => {
    const result = await sortWorker(numbers); // non-blocking UI
    console.log(result);
  };

  return (
    <button type="button" onClick={runSort}>
      Run Sort
    </button>
  );
};

๐Ÿ–ผ Live Demo

useworker demo


๐Ÿพ Examples

Edit white-glitter-icji4

More examples: https://github.com/alewin/useWorker/tree/develop/example


๐Ÿ”ง Roadmap

  • Kill Web Worker
  • Reactive web worker status
  • Add timeout option
  • Import and use remote script inside useWorker function
  • support Transferable Objects
  • Testing useWorker #41
  • Import and use local script inside useWorker function #37
  • useWorkers Hook #38
  • useWorkerFile Hook #93

๐Ÿค” Motivation and Limitations

Most react projects are initialized through Create React App. CRA unfortunately does not offer support for webworkers, unless you eject and change the webpack configuration manually.

This library allows you to use web workers without having to change the CRA configuration, which is why there are often limitations or particular workarounds.

If you are interested in changing the webpack configuration to manually manage your workers, see: worker-loader


Known issues

There's a known issue related to transpiling tools such as Babel causing Not refereced errors.

Since the approach of this library is moving the entire function passed to the Hook to a worker, if the function gets transpiled, variable definitions used by the transpiling tool may get out of scope when the function gets moved to the worker, causing unexpected reference errors.

If you're experimenting this type of issue, one workaround is wrapping your function declaration inside a function object as a string.

const sum = new Function(`a`, `b`, `return a + b`)

๐ŸŒ Contribute? Bug? New Feature?

The library is experimental so if you find a bug or would like to request a new feature, open an issue


๐Ÿ’ก Similar Projects


๐Ÿ’ป Mantainers

๐Ÿ’ป Contributors

  • Thanks to:
  • @zant (test, CI, RFC, bugfixes, localdependencies feature, ...)
  • @101arrowz ( isoworker packages proposal )
  • @z4o4z (Typescript implementation, Discussion of RFC)
  • @IljaDaderko (Typescript support, Discussion of RFC)
  • @ophirg (Typescript support)
  • @Pigotz (Discussion of RFC)

๐Ÿ“œ License

MIT


Netlify Status

Issues
  • RFC: Inline dependencies

    RFC: Inline dependencies

    Hey everyone! I'm on my way to implement the Inline Dependencies feature from #37, which I decided to implement after a discussion on #69. (Details there).

    I just finished the actual code and it's working just fine. However, there are small points on which I will be really glad to hear some opinions.

    The first one is related to the way users will be accessing to the actual utils object, this is an example of how it works right now in my code:

    const foo = (data) => {
      // Works just fine
      console.log(utils.fn())
      return data;
    };
    
    const utils = {
      fn: (val) => console.log(val)
    };
    
    const App = () => {
      const [worker, { terminateWorker }] = useWorker(foo, {
        transferable: "auto",
        localDependencies: utils, // Note how we the utils pass it directly
      });
    }
    

    This works because I'm inlining whatever object localDependencies is, and inserting it into the blob code, this is how it looks:

    const utils = {fn: val => console.log(val),}
    

    As you can see, it's named utils and that's the reason why it works inside foo. It's important to note that the utils object called inside foo is not the object with the same name declared below. This is important because of the following.

    There can be the case in which the object passed to localDependencies isn't available in the scope of the foo function declaration, in which we will need to add a linter ignore rule, this of course, assuming that you're using a linter, which is fairly possible since CRA and most React libraries come with linters, as it almost required in modern development workflows.

    const foo = (data) => {
      // eslint-disable-next-line no-undef
      console.log(utils.fooUtils.fn())
      return data;
    };
    
    const fooUtils = {
      fn: (val) => console.log(val)
    };
    
    const App = () => {
      const [worker, { terminateWorker }] = useWorker(foo, {
        transferable: "auto",
        localDependencies: { fooUtils },
      });
    }
    

    The above will still work, but as you can see, accessing correctly the properties inside the utils object, which lives in worker land:

    // Trailing commas are in case there is more than one prop
    const utils = {fooUtils: {fn: val => console.log(val),},}
    

    That's really all of it, what do you think?

    IMO, it's fairly understandable because the object is really not living on the function declaration's scope, so I think it gives clarity to what's going on under the hood, and can also be nicely integrated with a coincidentally good naming of your utils object.

    Another option could also be to pass the object as the last argument of foo, but I think that can be annoying since it will be really hidden under the hood.

    Also, is utils a nice name for the inlined object in the worker? maybe localDependencies is a better suite?

    P.D.: For reference, the implementation is at branch feature/27

    RFC 
    opened by zant 50
  • Why on each call to the worker the dependencies are re-fetched?

    Why on each call to the worker the dependencies are re-fetched?

    Hi! First of all thanks for this awesome library!

    I would like to ask something that I don't clearly understand. It's related to how the useWorker manages external dependencies.

    I clearly don't understand why, but, when I make a recall to the function given from the useWorker hook, for some reason it re-calls o re-imports the dependencies array.

    This behavior is also present in the external scripts example: https://icji4.csb.app/external

    If you open the network inspector, you can see when you click the button for Sorting Dates.

    Here you can see:

    Captura de Pantalla 2020-04-11 a la(s) 14 57 05

    What I would like to understand is that if this behavior is expected from the library and why? In case that not, I would submit a PR to fix this!

    Anyway, thanks again for your library!

    bug 
    opened by JonatanSalas 12
  • Proposal: Support for TransferList API

    Proposal: Support for TransferList API

    Based on #46.

    Introduction

    Transferable Objects are objects that can be passed to different contexts. This is useful in cases where we don't want to rely on structured cloning to pass data to a worker. Since this procedure moves data without copying it, it can provide better performance for critical applications (this is normally the case with really large objects).

    useWorker currently does not support a way for transferring these objects, since for the objects to get transferred, we need to pass a second argument to postMessage, that is the list of the objects we would like to transfer.

    worker.postMessage(message, transferList);
    

    We can see that the current implementation does not support passing a second argument to postMessage.

    https://github.com/alewin/useWorker/blob/ac082d640b737f5d2b27a4cb9a8d06b1dbb17c60/src/useWorker.ts#L114

    Solution

    Currently, the library provides a really nice way to execute functions inside a worker. Considering how it works:

    const addNumber = (arr, number) => return arr.map(el => el + number);
    
    const [addNumberWorker] = useWorker(addNumber)
    
    const arr = new Uint8Array([1, 2, 3, 4, 5]);
    // All the arguments we pass go directly to the function in the worker
    // So data.buffer will be just another argument instead of being moved to worker context
    addNumberWorker(arr, 1, [data.buffer]) // Does not transfer
    

    As we need a way to change the default functionality of the useWorker returned function, we introduce a new option to the options object.

    TransferList option

    The default will be false, and we opt-in to transferList.

    // We pass a new option to the worker
    const [addNumberWorker] = useWorker(addNumber, { transferList: true }); 
    

    Usage

    Option 1

    A first option could be to make the usage similar to how the native postMessage works. So when the worker gets called, we will pass the data property from the MessageEvent to the function.

    // Inside the worker
    onmessage = e => addNumber(e.data) // Not real implementation
    
    // When we call, we pass arguments as we would with postMessage
    const  data = { arr, number: 1 }
    addNumberWorker(data, transferList);
    

    However, this will restrict function implementations to just one argument, and so, developers will need to refactor a bit the implementation of the function if they want to opt-in:

    // Just one argument, they'll have to destructure it
    const addNumber = ({arr, number}) => arr.map(el => el + number)
    

    So we can consider this other approach.

    Option 2

    // Passing the arguments as elements of an array
    addNumberWorker([arr, 1], [arr.buffer]);
    

    With the above, we do not pass the data object to the function they provide, instead we collect the elements of the array and pass them as arguments of the function they provide. This way, developers can use the same function without any code changes with the added functionality to pass a transferList.

    But we can also argue that it feels weird to pass arguments as an array, besides, it's not really a wide used pattern on Javascript. So we can consider something else.

    Option 3

    const addNumber = (arr, number) => return arr.map(el => el + number);
    
    const [addNumberWorker] = useWorker(addNumber, { transferList: true })
    
    const arr = new Uint8Array([1, 2, 3, 4, 5]);
    /// Using as we do today with an added argument
    addNumberWorker(arr, 1, [arr.buffer]);
    

    Where the last argument will always be the transferList, when using the optional argument. I think this will be a familiar approach for current users. However, we really need to think about edge cases, as relying on argument can be a little fragile if not implemented carefully.

    Conclusion

    As you can see, I haven't get to a conclusion myself. Although I personally like Option 3 more, there are some consideration we need to make to introduce this feature in a way that both makes sense for developers, and doesn't feels foreign to how the hook works today.

    I'd love to hear your thoughts! Thanks for reading!

    Note: Besides browser compatibility seems to be fine we may want to provide feature detecting errors so we can spot bugs on older versions and relieve that burden from developers who use this hook.

    Reference:

    enhancement Proposal 
    opened by zant 9
  • wip: add ts support

    wip: add ts support

    this PR adds support for TS(#26), it generates normals JS build but with typescript definitions

    closes #25

    opened by z4o4z 7
  • Handle runtime errors in worker

    Handle runtime errors in worker

    Hey! This is my attempt to solve #21

    Previously, the callback was enclosed with a Promise.resolve which wasn't catching the errors that will possibly happen in callback. What I did instead was changing the Promise.resolve to a self invoked function that returns a new Promise, and chained the promise handlers there.

    As a side note, a function declaration and a subsequent call is arguably more readable but added the IIFE instead to match how the createWorkerBlobUrl was written.

    Let me know what you think! @alewin

    OBS: If the error is not handled in the client's code, this error is displayed in the console: net::ERR_FILE_NOT_FOUND. Seems to be caused by this call URL.revokeObjectURL(worker.current._url) in killWorker but not sure of why it's different from normally calling killWorker.

    opened by zant 6
  • No OffscreenCanvas compatibility detection

    No OffscreenCanvas compatibility detection

    error info Unhandled Rejection (ReferenceError): OffscreenCanvas is not defined

    bug 
    opened by aofong 5
  • Error's with async functions

    Error's with async functions

    I was trying to debug this to find out the cause, but was unsuccessful, basically when useWorker is passed a simple function like this everything is working:

    function sum(num1: number, num2: number) {
      return num1 + num2;
    }
    // ...
    const [sumWorker] = useWorker(sum);
    const result = await sumWorker(1, 2);
    

    However, I was trying to utilise worker to load images in a worker i.e

    async function fetchImage(src: string) {
      const response = await fetch(src);
      const blob = await response.blob();
      const objectUrl = URL.createObjectURL(blob);
    
      return objectUrl;
    }
    
    // ...
    const [fetchImageWorker] = useWorker(fetchImage);
    const result = await fetchImageWorker("https://...");
    

    I am getting a babel / webpack error:

    ReferenceError: regeneratorRuntime is not defined

    opened by IljaDaderko 5
  • Guidance on how to use local dependencies?

    Guidance on how to use local dependencies?

    Proposal 3 on https://github.com/alewin/useWorker/issues/36 mentions local dependencies

    I would like to use local dependencies to avoid having long functions which are hard to test / debug.

    I've tried to use the functionality but I get an object can not be cloned error.

    Guidance or an example on how use local dependencies would be helpful. Thank you.

    Code below โ€” https://codesandbox.io/s/hopeful-night-l6vw6?file=/src/App.js

    import React, { useState } from "react";
    import "./styles.css";
    import { useWorker } from "@koale/useworker";
    
    export default function App() {
      const [message, setMessage] = useState("waiting for go...");
      const [processTextWorker] = useWorker(fnWithLocalDependency, {
        localDependencies: [{ util: util }]
      });
      const handleClick = _ => {
        setMessage("working...");
        processTextWorker("blah", util).then(setMessage);
      };
      return (
        <div className="App">
          <h1>Hello CodeSandbox</h1>
          <h2>Start editing to see some magic happen!</h2>
          <button onClick={handleClick}>go!</button>
          <p>{message}</p>
        </div>
      );
    }
    
    const fnWithLocalDependency = text => {
      const a = util.fn1(text);
      const b = util.fn2(text);
      return JSON.stringify({ a, b });
    };
    const util = {
      fn1: text => `the text is '${text}'`,
      fn2: text => `:${text}:`
    };
    
    
    question 
    opened by marco-m-alves 5
  • Feature: onCreate function

    Feature: onCreate function

    Based on the discussion on #43, I added a new field to the API. It's called onCreate (help with naming is appreciated ๐Ÿ˜…).

    Description

    We introduce a new onCreate field to the options object. onCreate will be a function whose body is intended to be evaluated once when the worker is created.

    The body of the function is extracted as a string and added to the worker blob after the importScripts string. So we have every dependency available in the onCreate body.

    The reason it's not just called but the body is extracted as a string is because we want to make the function body available in the global scope of the worker. This way, the expressions in the body are evaluated when the worker is created, and the scope of every variable evaluated will be the global worker scope, which is persisted through the whole life of the worker.

    This API is expected to be used with autoTerminate: false but not restricted to it. Should we restrict it? Will there be any use case where this will be needed?

    Why

    If we set global variables on the worker creation, we can reuse them on subsequent calls to the worker, avoiding otherwise potentially expensive re-declarations. The section below provides an example with the fuse.js library.

    Usage

    // Here, we instantiate the (expensive) Fuse class once, on creation.
    // We also pass arguments to the constructor
    const onCreate = () => {
      const list = ["hi", "bye", "what", "main thread"];
      const options = { isCaseSensitive: true }
      // eslint-disable-next-line no-undef
      const fuse = new Fuse(list, options);
      console.log('Called once on creation!')
    };
    
    const fuseSearch = ({ input }) => {
      // Now we have access to the variables declared on the `onCreate` method.
      // eslint-disable-next-line no-undef
      return fuse.search(input);
    };
    
    const [fuseWorker, { kill }] = useWorker(fuseSearch, {
      onCreate,
      remoteDependencies: ["https://cdn.jsdelivr.net/npm/[email protected]"],
      autoTerminate: false,
    });
    
    const callWorker = async () => {
      const res = await fuseWorker({ input: "i" });
      console.log(res);
    };
    
    useEffect(() => () => kill(), []);
    

    In the example above, we also kept options and list on the global worker scope. Which may or not be useful. However, the general availability of declaring global variables enables all sorts of variations of similar patterns.

    Let me know your thoughts!

    opened by zant 4
  • [Proposal] migrate library to TypeScript

    [Proposal] migrate library to TypeScript

    Would you consider migrating this library to typescript to ensure better type safety and prevent some bugs from future contributors? I've started using this hook and am in โค๏ธwith how simple it is.

    In order to do this following "Todos" are probably required:

    • [x] Re-write source in TypeScript
    • [ ] Configure something like rollup to build js files for non typescript users
    • [x] Find a way to serve ts files to TypeScript users and built js files to other users
    • [ ] Configure CI to build js files for releases

    I can start helping out with Re-write source in TypeScript if you agree to proceed ๐Ÿ‘

    enhancement 
    opened by IljaDaderko 4
  • How can I use api calls in useWorker?

    How can I use api calls in useWorker?

    [Required] Describe the bug I need to get data from backend and store that large amount of data to indexeddb , But issue is when iam using axios or fetch iam getting error , is there any other option to invoke api calls ?

    "Uncaught ReferenceError: axios__WEBPACK_IMPORTED_MODULE_2___default is not defined" --- this error getting when using axios ,

    [Required] Expected behavior is there any inbuild method to call api ? ot any way to import third party library like axios

    [Optional] Screenshots

    [Optional] Device (please complete the following information):

    • OS: [Windows]
    • Browser [chrome]

    [Optional] context Add any other context about the problem here.

    bug 
    opened by KbnCodes 0
  • Queue tasks or abort current task and start new task

    Queue tasks or abort current task and start new task

    So I'm just getting my head around a problem I'm having with the useWorker hook. I currently call the worker in a useEffect hook but when the useEffect hook is triggered multiple times while the worker hasn't finished yet I'm getting the following error in the log:

    [useWorker] You can only run one instance of the worker at a time, if you want to run more than one in parallel, create another instance with the hook useWorker(). Read more: https://github.com/alewin/useWorker 
    

    The problem is I'm no longer interested in the result if the useEffect hook is triggered again so the worker should just abort the current task and start processing the new task. Or if that's not possible I want to queue a task for the worker to process next. Really curious if this can be achieved with the current hook or if I'm overlooking something.

    question 
    opened by jordijansen 0
  • Fix dependency cycle

    Fix dependency cycle

    This PR fixes the following error:

    Screen Shot 2021-08-03 at 11 53 47

    By moving types to their own folder

    opened by zant 0
  • useWorker@next

    [email protected]

    • update packages
    • reintroduction of isoworker and localdeps feature thanks to @101arrowz (https://github.com/alewin/useWorker/pull/107 )
    • fix eslint and prettier issues
    opened by alewin 1
  • When will localDependencies return

    When will localDependencies return

    Migrating my app to NextJS I decided to move from comlink-loader to useWorker counting from the documentation that it had support for local dependencies; but I just found out that it was actually removed few weeks ago. When can we expect to see this feature back? I really need it otherwise useWorker doesn't do it for me.

    question 
    opened by rodrigonzalz 2
  • Fix issues with isoworker

    Fix issues with isoworker

    I'm not sure what tooling to install to test this locally, so I'm just creating this PR to show the most appropriate usage of isoworker. I don't know how to set up Karma with Mocha, but the build looks fine after bundling, and if it fails that's fixable via https://github.com/reactioncommerce/reaction-component-library/issues/399#issuecomment-467860022.

    As a side note, you may want to consider hiding isoworker behind a feature flag that adds local dependency support, since that will reduce bundle size by quite a bit. isoworker, in the name of supporting everything that could possibly need to be serialized, adds around 3kB gzipped to the overall bundle size.

    opened by 101arrowz 1
  • Bad isoworker import

    Bad isoworker import

    [Required] Describe the bug isoworker dependency does not provide a default import so isoworker.createContext raises an error from index.mjs. I face this bug while using expo-camera bar code scanner on Web.

    [Required] Expected behavior No error.

    isoworker should be imported as import * as isoworker from 'isoworker' here https://github.com/alewin/useWorker/blob/011d2a39be6c7e4113f81dc52c56a2b9ba8ed209/packages/useWorker/src/lib/createWorkerBlobUrl.ts#L1 At least until isoworker has a default export cf https://github.com/101arrowz/isoworker/issues/2

    bug 
    opened by jer-sen 10
  • useWorker error when destructuring function arguments

    useWorker error when destructuring function arguments

    [Required] Describe the bug When passing a function to useWorker that has argument destructuring I get the error shown on the screenshot.

    [Optional] To Reproduce Steps to reproduce the behavior:

    1. Go to https://codesandbox.io/s/useworker-argument-destructuring-guznf
    2. Click on "Run test"
    3. See error

    [Required] Expected behavior Destructuring function arguments break the worker.

    [Optional] Screenshots This is the error I get in my project: image

    transpiling related 
    opened by juansalvatore 4
  • example for using remote web worker ( stockfish js)

    example for using remote web worker ( stockfish js)

    i try to use this lib https://github.com/nmrugg/stockfish.js/ as a web worker, but it not working. could you have the same example which using normal web worker like postMessage and onmessage, thanks in advance

    question 
    opened by hunght 3
Releases(@koale/[email protected])
Owner
Alessio Koci
Hi! I'm Alessio, Frontend Developer from Bologna
Alessio Koci
๐Ÿ“‹ React Hooks for forms validation (Web + React Native)

Performant, flexible and extensible forms with easy to use validation. English | ็นไธญ | ็ฎ€ไธญ | ๆ—ฅๆœฌ่ชž | ํ•œ๊ตญ์–ด | Franรงais | Italiano | Portuguรชs | Espaรฑol | ะ ัƒั

React Hook Form 23.6k Oct 14, 2021
React Hook for accessing state and dispatch from a Redux store

redux-react-hook React hook for accessing mapped state and dispatch from a Redux store. Table of Contents Install Quick Start Usage StoreContext useMa

Facebook Incubator 2.2k Oct 4, 2021
Essential React custom hooks โš“ to super charge your components!

Essential React custom hooks โš“ to super charge your components!

Bhargav Ponnapalli 1.9k Oct 16, 2021
Custom React hooks for your project.

Captain hook Overview Here is a modest list of hooks that I use every day. I will add more in the next few days, keep watching. And if you have some g

Steven Persia 338 Sep 29, 2021
React hook library, ready to use, written in Typescript.

This is the repository for usehooks.ts, a Gatsby powered blog hosted with Github & netlify that publishes easy to understand React Hook code snippets.

Julien 550 Oct 13, 2021
Testing hooks with Jest

Jooks (Jest โค + Hooks ????) If you're going through hell testing React Hooks, keep going. (Churchill) What are Custom React Hooks React Hooks are a ne

Antoine Jaussoin 74 Oct 9, 2021
A React.js global state manager with Hooks

A React.js global state manager with Hooks

Lorenzo Spinelli 60 Sep 13, 2021
React hooks for Material UI

React hooks for Material UI Install npm install use-mui or yarn add use-mui Supported components For each state, each hook accepts an optional default

Charles Stover 43 Oct 15, 2021
๐Ÿญ React hook that tracks mouse events on selected element - zero dependencies

React Mighty Mouse React hook that tracks mouse events on selected element. Demo Demos created with React DemoTab ?? Install npm install react-hook-mi

mkosir 85 Sep 6, 2021
๐Ÿง˜Managed, cancelable and safely typed requests.

Managed, cancelable and safely typed requests. Table of Contents Install Quick Start Usage useResource useRequest request() createRequestError() Type

Matheus Schettino 111 Aug 19, 2021
React hook for conveniently use Fetch API

react-fetch-hook React hook for conveniently use Fetch API. Tiny (556 B). Calculated by size-limit Both Flow and TypeScript types included import Reac

Ilya Lesik 303 Oct 3, 2021
use css in js with react hook.

style-hook easy to write dynamic css features use css in react hook easy to get started install use npm npm i -S style-hook or use yarn yarn add style

null 15 Sep 1, 2021
React custom hooks for async functions with abortability and composability

react-hooks-async React custom hooks for async functions with abortability and composability Introduction JavaScript promises are not abortable/cancel

Daishi Kato 492 Sep 28, 2021
Easily manage the Google Tag Manager via Hook

React Google Tag Manager Hook Use easily the Google Tag Manager With this custom hook, you can easily use the Google Tag Manager with 0 config, you ju

Guido Porcaro 105 Oct 13, 2021
๐Ÿ‘ฉโ€๐Ÿณ A React Hooks utility library containing popular customized hooks

React Recipes A React Hooks utility library containing popular customized hooks What's your favorite dish? npm i react-recipes --save yarn add react-r

Craig Walker 744 Oct 3, 2021
React hook to handle any async operation in React components, and prevent race conditions

React-async-hook This library only does one small thing, and does it well. Don't expect it to grow in size, because it is feature complete: Handle fet

Sรฉbastien Lorber 917 Oct 6, 2021
โš›๏ธ Minimal "optimistic UI" pattern implementation with a React Hook

react-optimistic-ui-hook Minimal zero dependency "optimistic UI" pattern implementation in a React Hook. What is "Optimistic UI"? Optimistic UIs donโ€™t

Mohamad Jahani 21 Sep 7, 2021
React hook for using keyboard shortcuts in components.

react-hotkeys-hook React hook for using keyboard shortcuts in components. This is a hook version for the hotkeys package. Documentation and live examp

Johannes Klauss 843 Oct 13, 2021
๐Ÿ˜Ž ๐Ÿ“ React hook for Google Maps Places Autocomplete.

USE-PLACES-AUTOCOMPLETE This is a React hook for Google Maps Places Autocomplete, which helps you build a UI component with the feature of place autoc

Welly 874 Oct 12, 2021