ahooks#useUpdate TypeScript Examples

The following examples show how to use ahooks#useUpdate. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: use-url-state.ts    From bext with MIT License 7 votes vote down vote up
useUrlState = <S extends UrlState = UrlState>(
  initialState?: S | (() => S),
  options?: Options,
) => {
  type State = Partial<{ [key in keyof S]: any }>;
  const { navigateMode = 'push' } = options || {};
  const history = useHistory();
  const update = useUpdate();

  const initialStateRef = useRef(
    typeof initialState === 'function'
      ? (initialState as () => S)()
      : initialState || {},
  );

  const queryFromUrl = useMemo(() => {
    return parse(location.search, parseConfig);
  }, [location.search]);

  const targetQuery: State = useMemo(
    () => ({
      ...initialStateRef.current,
      ...queryFromUrl,
    }),
    [queryFromUrl],
  );

  const setState = (s: React.SetStateAction<State>) => {
    const newQuery = typeof s === 'function' ? s(targetQuery) : s;
    update();
    history[navigateMode]({
      hash: location.hash,
      search: stringify({ ...queryFromUrl, ...newQuery }, parseConfig) || '?',
    });
  };

  return [targetQuery, useMemoizedFn(setState)] as const;
}
Example #2
Source File: useSpreadSheet.ts    From S2 with MIT License 4 votes vote down vote up
export function useSpreadSheet(props: SheetComponentsProps) {
  const forceUpdate = useUpdate();
  const s2Ref = React.useRef<SpreadSheet>();
  const containerRef = React.useRef<HTMLDivElement>();
  const wrapperRef = React.useRef<HTMLDivElement>();

  const {
    spreadsheet: customSpreadSheet,
    dataCfg,
    options,
    themeCfg,
    sheetType,
    onSheetUpdate = identity,
  } = props;
  /** 保存重渲 effect 的 deps */
  const updatePrevDepsRef = React.useRef<[S2DataConfig, S2Options, ThemeCfg]>([
    dataCfg,
    options,
    themeCfg,
  ]);

  const { loading, setLoading } = useLoading(s2Ref.current, props.loading);
  const pagination = usePagination(s2Ref.current, props);

  useEvents(props, s2Ref.current);

  const renderSpreadSheet = React.useCallback(
    (container: HTMLDivElement) => {
      const s2Options = getSheetComponentOptions(options);
      const s2Constructor: S2Constructor = [container, dataCfg, s2Options];
      if (customSpreadSheet) {
        return customSpreadSheet(...s2Constructor);
      }
      if (sheetType === 'table') {
        return new TableSheet(container, dataCfg, s2Options);
      }
      return new PivotSheet(container, dataCfg, s2Options);
    },
    [sheetType, options, dataCfg, customSpreadSheet],
  );

  const buildSpreadSheet = React.useCallback(() => {
    setLoading(true);
    s2Ref.current = renderSpreadSheet(containerRef.current);
    s2Ref.current.setThemeCfg(props.themeCfg);
    s2Ref.current.render();
    setLoading(false);

    // 子 hooks 内使用了 s2Ref.current 作为 dep
    // forceUpdate 一下保证子 hooks 能 rerender
    forceUpdate();

    props.getSpreadSheet?.(s2Ref.current);
  }, [props, renderSpreadSheet, setLoading, forceUpdate]);

  // init
  React.useEffect(() => {
    buildSpreadSheet();
    return () => {
      s2Ref.current.destroy();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // 重渲 effect:dataCfg, options or theme changed
  useUpdateEffect(() => {
    const [prevDataCfg, prevOptions, prevThemeCfg] = updatePrevDepsRef.current;
    updatePrevDepsRef.current = [dataCfg, options, themeCfg];

    let reloadData = false;
    let reBuildDataSet = false;
    if (!Object.is(prevDataCfg, dataCfg)) {
      reloadData = true;
      s2Ref.current?.setDataCfg(dataCfg);
    }

    if (!Object.is(prevOptions, options)) {
      if (!Object.is(prevOptions?.hierarchyType, options?.hierarchyType)) {
        // 自定义树目录需要重新构建 CustomTreePivotDataSet
        reBuildDataSet = true;
        reloadData = true;
        s2Ref.current?.setDataCfg(dataCfg);
      }
      s2Ref.current?.setOptions(options);
      s2Ref.current?.changeSheetSize(options.width, options.height);
    }
    if (!Object.is(prevThemeCfg, themeCfg)) {
      s2Ref.current?.setThemeCfg(themeCfg);
    }

    /**
     * onSheetUpdate 交出控制权
     * 由传入方决定最终的 render 模式
     */
    const renderOptions = onSheetUpdate({
      reloadData,
      reBuildDataSet,
    });

    s2Ref.current?.render(renderOptions.reloadData, {
      reBuildDataSet: renderOptions.reBuildDataSet,
    });
  }, [dataCfg, options, themeCfg, onSheetUpdate]);

  useResize({
    s2: s2Ref.current,
    container: containerRef.current,
    wrapper: wrapperRef.current,
    adaptive: props.adaptive,
  });

  return {
    s2Ref,
    containerRef,
    wrapperRef,
    loading,
    setLoading,
    pagination,
  };
}
Example #3
Source File: YakBatchExecutors.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
BugTestExecutor: React.FC<YakBatchExecutorsProp> = (props) => {
    const update = useUpdate()

    const [listHeight, setListHeight] = useState<number>(500)
    const [params, setParams] = useState<ExecBatchYakScriptParams>({
        Concurrent: 5,
        Keyword: props.keyword.split("-")[0],
        Limit: 10000,
        Target: props.sendTarget ? JSON.parse(props.sendTarget).join(",") : "",
        DisableNucleiWorkflow: true,
        ExcludedYakScript: [
            "[fingerprinthub-web-fingerprints]: FingerprintHub Technology Fingerprint",
            "[tech-detect]: Wappalyzer Technology Detection"
        ],
        TotalTimeoutSeconds: 180,
        Type: "nuclei"
    })
    const [totalLoading, setTotalLoading] = useState(true)
    const [tasks, setTasks, getTasks] = useGetState<ExecBatchYakScriptTask[]>([])
    const [filterTasks, setFilterTasks, getFilterTasks] = useGetState<ExecBatchYakScriptTask[]>([])
    const [error, setError] = useState("")
    const [token, setToken] = useState("")
    const [executing, setExecuting] = useState(false)
    const [checked, setChecked] = useState<boolean>(false)

    const [uploadLoading, setUploadLoading] = useState(false)

    const containerRef = useRef(null)
    const wrapperRef = useRef(null)
    const listRef = useRef(null)
    const filterContainerRef = useRef(null)
    const filterWrapperRef = useRef(null)

    const [list] = useVirtualList(getTasks(), {
        containerTarget: containerRef,
        wrapperTarget: wrapperRef,
        itemHeight: 50,
        overscan: 5
    })
    const [filterList] = useVirtualList(getFilterTasks(), {
        containerTarget: filterContainerRef,
        wrapperTarget: filterWrapperRef,
        itemHeight: 50,
        overscan: 5
    })

    window.onresize = () => {
        let timer: any = null
        window.onresize = () => {
            if (timer) clearTimeout(timer)
            timer = setTimeout(() => {
                if (!listRef || !listRef.current) return
                const list = listRef.current as unknown as HTMLDivElement
                setListHeight(list.clientHeight - 10)
            }, 50)
        }
    }

    useEffect(() => {
        setTimeout(() => {
            if (!listRef || !listRef.current) return
            const list = listRef.current as unknown as HTMLDivElement
            setListHeight(list.clientHeight - 10)
        }, 600)

        return () => {
            window.onresize = null
        }
    }, [])

    useEffect(() => {
        let timer = setTimeout(() => {
            update()
        }, 100)
        return () => {
            clearTimeout(timer)
        }
    }, [listHeight])

    useEffect(() => {
        setTotalLoading(true)
        setTimeout(() => setTotalLoading(false), 500)

        const token = randomString(40)
        setToken(token)
        setTasks([])
        setParams({...params, Keyword: props.keyword.split("-")[0]})
        const tempTasks = new Map<string, ExecBatchYakScriptTask>()
        const updateTasks = () => {
            let items: ExecBatchYakScriptTask[] = []
            tempTasks.forEach((v, k) => {
                items.push(v)
            })
            setTasks(items.sort((a, b) => b.Id.localeCompare(a.Id)))
        }
        const dataChannel = `${token}-exec-batch-yak-script-data`
        const errorChannel = `${token}-exec-batch-yak-script-error`
        const endChannel = `${token}-exec-batch-yak-script-end`

        let updateTableTick = setInterval(updateTasks, 1000)
        ipcRenderer.on(dataChannel, async (e: any, data: ExecBatchYakScriptResult) => {
            if (data.ProgressMessage) {
                return
            }
            let element = tempTasks.get(data.Id)
            if (element === undefined) {
                tempTasks.set(data.Id, {
                    Id: data.Id,
                    PoC: data.PoC,
                    Results: [],
                    Status: data.Status,
                    progress: 0
                })
                // updateTasks()
                return
            } else {
                element.Status = data.Status

                if (!element.Ok) {
                    element.Ok = data.Ok || false
                }
                element.Reason = data.Reason

                if (data.Result) {
                    element.Results.push({...data.Result})
                }
                // updateTasks()
                return
            }
        })
        ipcRenderer.on(errorChannel, (e: any, error: any) => {
            setError(error)
        })
        ipcRenderer.on(endChannel, (e: any, data: any) => {
            info("模块加载完成 / 执行完毕")
            setExecuting(false)
            updateTasks()
        })
        ipcRenderer.invoke(
            "exec-batch-yak-script",
            {...params, Keyword: props.keyword.split("-")[0], Target: ""},
            token
        )
        setExecuting(true)

        return () => {
            clearInterval(updateTableTick)
            ipcRenderer.invoke("cancel-exec-batch-yak-script", token)
            ipcRenderer.removeAllListeners(dataChannel)
            ipcRenderer.removeAllListeners(errorChannel)
            ipcRenderer.removeAllListeners(endChannel)
        }
    }, [props.keyword])

    // 转换task内的result数据
    const convertTask = (task: ExecBatchYakScriptTask) => {
        // @ts-ignore
        const results: ExecResult[] = task.Results

        const messages: ExecResultMessage[] = []
        for (let item of results) {
            if (!item.IsMessage) continue

            try {
                const raw = item.Message
                const obj: ExecResultMessage = JSON.parse(Buffer.from(raw).toString("utf8"))
                messages.push(obj)
            } catch (e) {
                console.error(e)
            }
        }

        return messages
    }

    useEffect(() => {
        if (checked) {
            const filters: ExecBatchYakScriptTask[] = getTasks()
                .filter((item) => item.Results.length !== 0)
                .filter(
                    (item) =>
                        (
                            convertTask(item)
                                .filter((e) => e.type === "log")
                                .map((i) => i.content)
                                .sort((a: any, b: any) => a.timestamp - b.timestamp) as ExecResultLog[]
                        ).filter((i) => ["json", "success"].includes((i?.level || "").toLowerCase())).length > 0
                )
            setFilterTasks(filters)
        } else {
            setFilterTasks([])
        }
    }, [checked])

    useEffect(() => {
        if (tasks) {
            const filters: ExecBatchYakScriptTask[] = getTasks()
                .filter((item) => item.Results.length !== 0)
                .filter(
                    (item) =>
                        (
                            convertTask(item)
                                .filter((e) => e.type === "log")
                                .map((i) => i.content)
                                .sort((a: any, b: any) => a.timestamp - b.timestamp) as ExecResultLog[]
                        ).filter((i) => ["json", "success"].includes((i?.level || "").toLowerCase())).length > 0
                )
            if (JSON.stringify(filterTasks) !== JSON.stringify(filters)) setFilterTasks(filters)
        }
    }, [tasks])

    if (totalLoading) {
        return (
            <div style={{textAlign: "center", width: "100%", marginTop: 100}}>
                <Spin>正在加载专用漏洞库</Spin>
            </div>
        )
    }

    return (
        <div className='bug-test-container'>
            <Row>
                <Col span={3}></Col>
                <Col span={18}>
                    <Form
                        style={{textAlign: "center"}}
                        onSubmitCapture={(e) => {
                            e.preventDefault()

                            if (tasks.length === 0) {
                                Modal.error({title: "模块还未加载,请点击右上角配置进行YAML POC更新"})
                                return
                            }

                            if (!params.Target) {
                                Modal.error({title: "检测目标不能为空"})
                                return
                            }
                            if (!params.Keyword) {
                                Modal.error({title: "无 PoC 关键字选择"})
                                return
                            }
                            if (!token) {
                                Modal.error({title: "BUG:无 Token 生成,请重新打开该页"})
                            }
                            ipcRenderer.invoke("exec-batch-yak-script", params, token)
                            setExecuting(true)
                            setChecked(false)
                        }}
                    >
                        <Space style={{width: "80%"}} direction={"vertical"}>
                            <Spin spinning={uploadLoading}>
                                <ContentUploadInput
                                    type="textarea"
                                    beforeUpload={(f) => {
                                        if (f.type !== "text/plain") {
                                            failed(`${f.name}非txt文件,请上传txt格式文件!`)
                                            return false
                                        }

                                            setUploadLoading(true)
                                            ipcRenderer.invoke("fetch-file-content", (f as any).path).then((res) => {
                                                setParams({...params, Target: res})
                                                setTimeout(() => setUploadLoading(false), 100)
                                            })
                                            return false
                                        }}
                                        item={{
                                            style: {textAlign: "left"},
                                            label: "检测的目标",
                                        }}
                                        textarea={{
                                            isBubbing: true,
                                            setValue: (Target) => setParams({...params, Target}),
                                            value: params.Target,
                                            rows: 1,
                                            placeholder: "可接受输入为:URL / IP / 域名 / 主机:端口,逗号分隔"
                                        }}
                                        suffixNode={
                                            executing ? (
                                                <Popconfirm
                                                    title={"确定要停止该漏洞检测?"}
                                                    onConfirm={(e) => ipcRenderer.invoke("cancel-exec-batch-yak-script", token)}
                                                >
                                                    <Button type='primary' danger>
                                                        强制停止
                                                    </Button>
                                                </Popconfirm>
                                            ) : (
                                                <Button type='primary' htmlType='submit'>
                                                    开始检测
                                                </Button>
                                            )
                                        }
                                ></ContentUploadInput>
                            </Spin>
                            <div style={{width: "100%", textAlign: "left", paddingLeft: 84}}>
                                <Space>
                                    <Tag>并发/线程: {params.Concurrent}</Tag>
                                    <Tag>总超时: {params.TotalTimeoutSeconds} sec</Tag>
                                    <Button
                                        type={"link"}
                                        style={{margin: 0, paddingLeft: 0}}
                                        onClick={(e) => {
                                            showModal({
                                                title: "设置批量检测额外参数",
                                                content: (
                                                    <>
                                                        <Form
                                                            onSubmitCapture={(e) => e.preventDefault()}
                                                            labelCol={{span: 7}}
                                                            wrapperCol={{span: 14}}
                                                        >
                                                            <InputInteger
                                                                label={"并发量(线程)"}
                                                                setValue={(Concurrent) =>
                                                                    setParams({...params, Concurrent})
                                                                }
                                                                defaultValue={params.Concurrent}
                                                            />
                                                            <InputInteger
                                                                label={"总超时时间/s"}
                                                                setValue={(TotalTimeoutSeconds) =>
                                                                    setParams({
                                                                        ...params,
                                                                        TotalTimeoutSeconds
                                                                    })
                                                                }
                                                                defaultValue={params.TotalTimeoutSeconds}
                                                            />
                                                        </Form>
                                                    </>
                                                )
                                            })
                                        }}
                                    >
                                        额外参数
                                    </Button>
                                </Space>
                            </div>
                        </Space>
                    </Form>
                </Col>
                <Col span={3} style={{position: "relative"}}>
                    <div style={{width: 140, position: "absolute", right: 2, bottom: 2}}>
                        <span style={{display: "inline-block", height: 22, marginRight: 5}}>只展示命中项</span>
                        <Switch checked={checked} onChange={(checked) => setChecked(checked)}></Switch>
                    </div>
                </Col>
            </Row>
            <Divider style={{margin: "10px 0"}} />
            <div ref={listRef} className='bug-test-list'>
                {tasks.length === 0 ? (
                    <div>
                        <Empty
                            style={{marginTop: 75}}
                            description={"模块还未加载,请点击右上角配置进行插件仓库更新"}
                        ></Empty>
                    </div>
                ) : checked ? (
                    <div ref={filterContainerRef} style={{height: listHeight, overflow: "auto"}}>
                        <div ref={filterWrapperRef}>
                            {filterList.map((ele) => (
                                <div className='list-item' key={ele.data.Id}>
                                    <Text ellipsis={{tooltip: true}} copyable={true} style={{width: 260}}>
                                        {ele.data.Id}
                                    </Text>
                                    <Divider type='vertical' />
                                    <div style={{width: 120, textAlign: "center"}}>
                                        {StatusToVerboseTag(ele.data.Status)}
                                    </div>
                                    <Divider type='vertical' />
                                    <div>
                                        <ExecResultsViewer results={ele.data.Results} oneLine={true} />
                                    </div>
                                    <Divider type='vertical' />
                                    <div style={{flexGrow: 1, textAlign: "right"}}>
                                        <Space>
                                            <Button
                                                type={"primary"}
                                                size={"small"}
                                                onClick={(e) => {
                                                    if (!ele.data.PoC) {
                                                        Modal.error({title: "没有模块信息"})
                                                        return
                                                    }
                                                    showModal({
                                                        title: `单体模块测试: ${ele.data.PoC.ScriptName}`,
                                                        width: "75%",
                                                        content: (
                                                            <>
                                                                <YakScriptOperator script={ele.data.PoC} target={params.Target} />
                                                            </>
                                                        )
                                                    })
                                                }}
                                            >
                                                复测
                                            </Button>
                                            <Button
                                                size={"small"}
                                                style={{marginRight: 8}}
                                                onClick={(e) => {
                                                    if (!ele.data.PoC) {
                                                        Modal.error({title: "没有模块信息"})
                                                        return
                                                    }
                                                    showModal({
                                                        title: `源码: ${ele.data.PoC.ScriptName}`,
                                                        width: "75%",
                                                        content: (
                                                            <>
                                                                <div style={{height: 400}}>
                                                                    <YakEditor
                                                                        readOnly={true}
                                                                        type={"yaml"}
                                                                        value={ele.data.PoC.Content}
                                                                    />
                                                                </div>
                                                            </>
                                                        )
                                                    })
                                                }}
                                            >
                                                源码
                                            </Button>
                                        </Space>
                                    </div>
                                </div>
                            ))}
                        </div>
                    </div>
                ) : (
                    <div ref={containerRef} style={{height: listHeight, overflow: "auto"}}>
                        <div ref={wrapperRef}>
                            {list.map((ele) => (
                                <div className='list-item' key={ele.data.Id}>
                                    <Text ellipsis={{tooltip: true}} copyable={true} style={{width: 260}}>
                                        {ele.data.Id}
                                    </Text>
                                    <Divider type='vertical' />
                                    <div style={{width: 120, textAlign: "center"}}>
                                        {StatusToVerboseTag(ele.data.Status)}
                                    </div>
                                    <Divider type='vertical' />
                                    <div>
                                        <ExecResultsViewer results={ele.data.Results} oneLine={true} />
                                    </div>
                                    <Divider type='vertical' />
                                    <div style={{flexGrow: 1, textAlign: "right"}}>
                                        <Space>
                                            <Button
                                                type={"primary"}
                                                size={"small"}
                                                onClick={(e) => {
                                                    if (!ele.data.PoC) {
                                                        Modal.error({title: "没有模块信息"})
                                                        return
                                                    }
                                                    showModal({
                                                        title: `单体模块测试: ${ele.data.PoC.ScriptName}`,
                                                        width: "75%",
                                                        content: (
                                                            <>
                                                                <YakScriptOperator script={ele.data.PoC} target={params.Target} />
                                                            </>
                                                        )
                                                    })
                                                }}
                                            >
                                                复测
                                            </Button>
                                            <Button
                                                size={"small"}
                                                style={{marginRight: 8}}
                                                onClick={(e) => {
                                                    if (!ele.data.PoC) {
                                                        Modal.error({title: "没有模块信息"})
                                                        return
                                                    }
                                                    showModal({
                                                        title: `源码: ${ele.data.PoC.ScriptName}`,
                                                        width: "75%",
                                                        content: (
                                                            <>
                                                                <div style={{height: 400}}>
                                                                    <YakEditor
                                                                        readOnly={true}
                                                                        type={"yaml"}
                                                                        value={ele.data.PoC.Content}
                                                                    />
                                                                </div>
                                                            </>
                                                        )
                                                    })
                                                }}
                                            >
                                                源码
                                            </Button>
                                        </Space>
                                    </div>
                                </div>
                            ))}
                        </div>
                    </div>
                )}
            </div>
        </div>
    )
}