{"id":6876,"date":"2022-10-24T09:31:08","date_gmt":"2022-10-24T08:31:08","guid":{"rendered":"https:\/\/solidt.eu\/site\/?p=6876"},"modified":"2024-02-09T11:59:37","modified_gmt":"2024-02-09T10:59:37","slug":"react-hooks-helpers","status":"publish","type":"post","link":"https:\/\/solidt.eu\/site\/react-hooks-helpers\/","title":{"rendered":"React Hooks: Helpers"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">useDataSource()<\/h2>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"csharp\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">import { Dispatch, SetStateAction, useEffect, useState } from \"react\";\n\nexport interface IData&lt;T> {\n    data: T;\n    loading: boolean;\n    loaded: boolean;\n    setData: Dispatch&lt;SetStateAction&lt;T>>;\n    updateData: () => any;\n}\n\nexport function useData&lt;T>(fetchFn: () => Promise&lt;T>, deps: any = []): IData&lt;T> {\n    const [trigger, setTrigger] = useState&lt;boolean>(false);\n    const [data, setData] = useState&lt;T>();\n    const [loading, setLoading] = useState(true);\n    const [loaded, setLoaded] = useState(false);\n    useEffect(() => {\n        async function fetch() {\n            setLoading(true);\n            setLoaded(false);\n            try {\n                const newData = await fetchFn();\n                setData(newData);\n                setLoaded(true);\n            } catch (e) {\n                console.error(e);\n                setData(undefined);\n            } finally {\n                setLoading(false);\n            }\n        }\n        fetch();\n        \/\/ eslint-disable-next-line react-hooks\/exhaustive-deps\n    }, [...deps, trigger]);\n    return {\n        data,\n        loading,\n        loaded,\n        setData,\n        updateData: () => setTrigger(!trigger)\n    } as IData&lt;T>;\n};\n\n<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">useInterval()<\/h2>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"typescript\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">\/* eslint-disable react-hooks\/exhaustive-deps *\/\nimport { useEffect, useState } from \"react\";\n\nfunction useInterval(delay: number) {\n    const [data, setData] = useState&lt;number>(0);\n    let index = 0;\n    useEffect(() => {\n        if (!delay &amp;&amp; delay !== 0) { return; }\n        const id = setInterval(() => setData(index++), delay);\n        return () => clearInterval(id);\n    }, [delay]);\n    return [data, setData];\n}\nexport default useInterval;\n<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">useObservable() + useFetch() example<\/h2>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"typescript\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">\/* eslint-disable react-hooks\/exhaustive-deps *\/\nimport { useEffect, useState } from \"react\";\nimport { Observable } from \"rxjs\";\n\nimport { Select } from \"antd\";\nimport { IUser } from \"..\/..\/domain\/user\/IUser\";\nimport { appDomain, translate } from \"..\/..\/domain\/wiring\/AppDomain\";\n\n\nexport function useFetch&lt;T>(fetchFn: () => Promise&lt;T[]>, deps: any[] = []): [T[], Dispatch&lt;SetStateAction&lt;T[]>>] {\n    const [data, setData] = useState&lt;T[]>([]);\n    useEffect(() => {\n        async function fetch() {\n            const newData = await fetchFn();\n            setData(newData);\n        }\n        fetch();\n    }, deps);\n    return [data, setData];\n};\n\n\ninterface IProps {\n    value?: string;\n    onChange: (value: string) => void;\n}\nexport function UserSelector(props: IProps) {\n    const company = useObservable(appDomain.ICompanyService.companyObservable());\n    const [users] = useFetch&lt;IUser>(() => appDomain.IRequestUser.getAllUsersForCompany(company?.id), [company?.id]);\n    return &lt;Select\n        showSearch\n        style={{ minWidth: 200 }}\n        placeholder={translate(\"general.select.user\")}\n        optionFilterProp=\"children\"\n        value={props.value}\n        onChange={props.onChange}>\n        {users.map((x: IUser, i: number) => (&lt;Select.Option key={x.id} value={x.id!}>{x.fullName}&lt;\/Select.Option>))}\n    &lt;\/Select>;\n}\n\nexport function useObservable&lt;T>(subject: Observable&lt;T | undefined>, initialValue?: T): T | undefined {\n    const [data, setData] = useState&lt;T | undefined>(initialValue);\n    useEffect(() => {\n        if (!subject) { return; }\n        const subscription = subject.subscribe((x: any) => {\n            setData(x);\n        });\n        return () => subscription.unsubscribe();\n    }, [subject]);\n    return data;\n}\n\nexport function useObservableReducer&lt;T>(subject?: Observable&lt;T | undefined>): T | undefined {\n    const [data, forceUpdate] = useReducer(x => x + 1, 0);\n    useEffect(() => {\n        if (!subject) { return; }\n        const subscription = subject.subscribe(() => {\n            forceUpdate();\n        });\n        return () => subscription.unsubscribe();\n    }, [subject]);\n    return data;\n}\n\n<\/pre><\/div>\n\n\n\n<p>Old code (with useSubscription)<\/p>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"typescript\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">\/* eslint-disable react-hooks\/exhaustive-deps *\/\nimport { useEffect, useState, Dispatch, SetStateAction } from \"react\";\nimport { Subscription } from \"rxjs\";\n\nimport { Select } from \"antd\";\nimport { IUser } from \"..\/..\/domain\/user\/IUser\";\nimport { appDomain, translate } from \"..\/..\/domain\/wiring\/AppDomain\";\n\ntype ChangeFn&lt;T> = (next: T | undefined) => any;\ntype SubscribeFn&lt;T> = (nextFn: ChangeFn&lt;T>) => Subscription;\n\nexport function useSubscription&lt;T>(subscribeFn: SubscribeFn&lt;T>, deps?: any[]): T | undefined {\n    const [data, setData] = useState&lt;T | undefined>(undefined);\n    useEffect(() => {\n        function onChange(next: T | undefined) {\n            setData(next);\n        }\n        const subscription = subscribeFn(onChange);\n        return () => subscription.unsubscribe();\n    }, deps);\n    return data;\n};\n\nexport function useDatasource&lt;T>(fetchFn: () => Promise&lt;T[]>, deps: any[]): [T[], Dispatch&lt;SetStateAction&lt;T[]>>] {\n    const [data, setData] = useState&lt;T[]>([]);\n    useEffect(() => {\n        async function fetch() {\n            const newData = await fetchFn();\n            setData(newData);\n        }\n        fetch();\n    }, deps);\n    return [data, setData];\n};\n\n\/* Example usage *\/\n\ninterface IProps {\n    value?: string;\n    onChange: (value: string) => void;\n}\nexport function UserSelector(props: IProps) {\n    const company = useSubscription(appDomain.ICompanyService.subscribeToCompany.bind(appDomain.ICompanyService));\n    const [users] = useDatasource&lt;IUser>(() => appDomain.IRequestUser.getAllUsersForCompany(company?.id), [company?.id]);\n    return &lt;Select\n        showSearch\n        style={{ minWidth: 200 }}\n        placeholder={translate(\"general.select.user\")}\n        optionFilterProp=\"children\"\n        value={props.value}\n        onChange={props.onChange}>\n        {users.map((x: IUser, i: number) => (&lt;Select.Option key={x.id} value={x.id!}>{x.fullName}&lt;\/Select.Option>))}\n    &lt;\/Select>;\n}\n\n\n<\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"<p>useDataSource() useInterval() useObservable() + useFetch() example Old code (with useSubscription)<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-6876","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/6876","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/comments?post=6876"}],"version-history":[{"count":8,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/6876\/revisions"}],"predecessor-version":[{"id":8266,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/6876\/revisions\/8266"}],"wp:attachment":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/media?parent=6876"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/categories?post=6876"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/tags?post=6876"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}