如何将useReducer和rxjs与react挂钩一起使用?

如何将useReducer和rxjs与react挂钩一起使用?,rxjs,react-hooks,Rxjs,React Hooks,我想一起使用react钩子和rxjs中的useReducer。 例如,我想从API获取数据 这是我为此编写的代码: RXJS钩子: function useRx(createSink, data, defaultValue = null) { const [source, sinkSubscription] = useMemo(() => { const source = new Subject() const sink = createSink(s

我想一起使用react钩子和rxjs中的useReducer。 例如,我想从API获取数据

这是我为此编写的代码:

RXJS钩子:

function useRx(createSink, data, defaultValue = null) {
    const [source, sinkSubscription] = useMemo(() => {
        const source = new Subject()
        const sink = createSink(source.pipe(distinctUntilChanged()));
        const sinkSubscription = sink.subscribe()
        return [source, sinkSubscription]
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [])

    useEffect(() => {
        source.next(data)
    }, [source, data])

    useEffect(() => {
        return () => {
            sinkSubscription.unsubscribe()
        };
    }, [sinkSubscription])
}
const dataFetchReducer = (state, action) => {
    switch (action.type) {
        case 'FETCH_LOADING':
            return {
                ...state,
                loading: true
            };
        case 'FETCH_SUCCESS':
            return {
                ...state,
                loading: false,
                total: action.payload.total,
                data: action.payload.data
            };
        case 'FETCH_FAILURE':
            return {
                ...state,
                error: action.payload
            };
        case 'PAGE':
            return {
                ...state,
                page: action.page,
                rowsPerPage: action.rowsPerPage
            };
        default:
            throw new Error();
    }
};
function usePaginationReducerEndpoint(callbackService) {
    const defaultPagination = {
        statuses: null,
        page: 0,
        rowsPerPage: 10,
        data: [],
        total: 0,
        error: null,
        loading: false
    }
    const [pagination, dispatch] = useReducer(dataFetchReducer, defaultPagination)
    const memoPagination = useMemo(
        () => ({
            statuses: pagination.statuses,
            page: pagination.page,
            rowsPerPage: pagination.rowsPerPage
        }),
        [pagination.statuses, pagination.page, pagination.rowsPerPage]
    );
    useRx(
        memoPagination$ =>
        memoPagination$.pipe(
                map(memoPagination => {
                    dispatch({type: "FETCH_LOADING"})
                    return memoPagination
                }),
                switchMap(memoPagination => callbackService(memoPagination.statuses, memoPagination.page, memoPagination.rowsPerPage).pipe(
                    map(dataPagination => {
                        dispatch({ type: "FETCH_SUCCESS", payload: dataPagination })
                        return dataPagination
                    }),
                    catchError(error => {
                        dispatch({ type: "FETCH_SUCCESS", payload: "error" })
                        return of(error)
                    })
                ))
            ),
            memoPagination,
        defaultPagination,
        2000
    );
    function handleRowsPerPageChange(event) {
        const newTotalPages = Math.trunc(pagination.total / event.target.value)
        const newPage = Math.min(pagination.page, newTotalPages)
        dispatch({
            type: "PAGE",
            page: newPage,
            rowsPerPage: event.target.value
        });
    }
    function handlePageChange(event, page) {
        dispatch({
            type: "PAGE",
            page: page,
            rowsPerPage: pagination.rowsPerPage
        });
    }
    return [pagination, handlePageChange, handleRowsPerPageChange]
}
减速器代码:

function useRx(createSink, data, defaultValue = null) {
    const [source, sinkSubscription] = useMemo(() => {
        const source = new Subject()
        const sink = createSink(source.pipe(distinctUntilChanged()));
        const sinkSubscription = sink.subscribe()
        return [source, sinkSubscription]
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [])

    useEffect(() => {
        source.next(data)
    }, [source, data])

    useEffect(() => {
        return () => {
            sinkSubscription.unsubscribe()
        };
    }, [sinkSubscription])
}
const dataFetchReducer = (state, action) => {
    switch (action.type) {
        case 'FETCH_LOADING':
            return {
                ...state,
                loading: true
            };
        case 'FETCH_SUCCESS':
            return {
                ...state,
                loading: false,
                total: action.payload.total,
                data: action.payload.data
            };
        case 'FETCH_FAILURE':
            return {
                ...state,
                error: action.payload
            };
        case 'PAGE':
            return {
                ...state,
                page: action.page,
                rowsPerPage: action.rowsPerPage
            };
        default:
            throw new Error();
    }
};
function usePaginationReducerEndpoint(callbackService) {
    const defaultPagination = {
        statuses: null,
        page: 0,
        rowsPerPage: 10,
        data: [],
        total: 0,
        error: null,
        loading: false
    }
    const [pagination, dispatch] = useReducer(dataFetchReducer, defaultPagination)
    const memoPagination = useMemo(
        () => ({
            statuses: pagination.statuses,
            page: pagination.page,
            rowsPerPage: pagination.rowsPerPage
        }),
        [pagination.statuses, pagination.page, pagination.rowsPerPage]
    );
    useRx(
        memoPagination$ =>
        memoPagination$.pipe(
                map(memoPagination => {
                    dispatch({type: "FETCH_LOADING"})
                    return memoPagination
                }),
                switchMap(memoPagination => callbackService(memoPagination.statuses, memoPagination.page, memoPagination.rowsPerPage).pipe(
                    map(dataPagination => {
                        dispatch({ type: "FETCH_SUCCESS", payload: dataPagination })
                        return dataPagination
                    }),
                    catchError(error => {
                        dispatch({ type: "FETCH_SUCCESS", payload: "error" })
                        return of(error)
                    })
                ))
            ),
            memoPagination,
        defaultPagination,
        2000
    );
    function handleRowsPerPageChange(event) {
        const newTotalPages = Math.trunc(pagination.total / event.target.value)
        const newPage = Math.min(pagination.page, newTotalPages)
        dispatch({
            type: "PAGE",
            page: newPage,
            rowsPerPage: event.target.value
        });
    }
    function handlePageChange(event, page) {
        dispatch({
            type: "PAGE",
            page: page,
            rowsPerPage: pagination.rowsPerPage
        });
    }
    return [pagination, handlePageChange, handleRowsPerPageChange]
}
我如何混合它们:

function useRx(createSink, data, defaultValue = null) {
    const [source, sinkSubscription] = useMemo(() => {
        const source = new Subject()
        const sink = createSink(source.pipe(distinctUntilChanged()));
        const sinkSubscription = sink.subscribe()
        return [source, sinkSubscription]
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [])

    useEffect(() => {
        source.next(data)
    }, [source, data])

    useEffect(() => {
        return () => {
            sinkSubscription.unsubscribe()
        };
    }, [sinkSubscription])
}
const dataFetchReducer = (state, action) => {
    switch (action.type) {
        case 'FETCH_LOADING':
            return {
                ...state,
                loading: true
            };
        case 'FETCH_SUCCESS':
            return {
                ...state,
                loading: false,
                total: action.payload.total,
                data: action.payload.data
            };
        case 'FETCH_FAILURE':
            return {
                ...state,
                error: action.payload
            };
        case 'PAGE':
            return {
                ...state,
                page: action.page,
                rowsPerPage: action.rowsPerPage
            };
        default:
            throw new Error();
    }
};
function usePaginationReducerEndpoint(callbackService) {
    const defaultPagination = {
        statuses: null,
        page: 0,
        rowsPerPage: 10,
        data: [],
        total: 0,
        error: null,
        loading: false
    }
    const [pagination, dispatch] = useReducer(dataFetchReducer, defaultPagination)
    const memoPagination = useMemo(
        () => ({
            statuses: pagination.statuses,
            page: pagination.page,
            rowsPerPage: pagination.rowsPerPage
        }),
        [pagination.statuses, pagination.page, pagination.rowsPerPage]
    );
    useRx(
        memoPagination$ =>
        memoPagination$.pipe(
                map(memoPagination => {
                    dispatch({type: "FETCH_LOADING"})
                    return memoPagination
                }),
                switchMap(memoPagination => callbackService(memoPagination.statuses, memoPagination.page, memoPagination.rowsPerPage).pipe(
                    map(dataPagination => {
                        dispatch({ type: "FETCH_SUCCESS", payload: dataPagination })
                        return dataPagination
                    }),
                    catchError(error => {
                        dispatch({ type: "FETCH_SUCCESS", payload: "error" })
                        return of(error)
                    })
                ))
            ),
            memoPagination,
        defaultPagination,
        2000
    );
    function handleRowsPerPageChange(event) {
        const newTotalPages = Math.trunc(pagination.total / event.target.value)
        const newPage = Math.min(pagination.page, newTotalPages)
        dispatch({
            type: "PAGE",
            page: newPage,
            rowsPerPage: event.target.value
        });
    }
    function handlePageChange(event, page) {
        dispatch({
            type: "PAGE",
            page: page,
            rowsPerPage: pagination.rowsPerPage
        });
    }
    return [pagination, handlePageChange, handleRowsPerPageChange]
}
代码工作,但我想知道这是运气还是没有

  • 我可以在RXJS管道内部调度吗?风险是什么
  • 如果没有,我如何混合useReducer和RXJS
  • 如果这不是好方法,那么好方法是什么
我知道这个消息。但是我想混合使用hooks和RXJS的功能,以便在异步请求中使用RXJS的debounce函数


感谢您的帮助,

您只需要一个中间件来连接useReducer和rxjs,而不是自己创建一个。 使用useReducer将创建大量潜在的难以调试的代码,并且还需要一个独立的容器组件来放置useReducer,以防止意外的全局重新渲染

因此,我建议使用redux让useReducer从组件创建全局状态,并使用redux observable(基于RxJS 6的redux中间件)作为中间件来连接RxJS和redux

如果您熟悉rxjs,它将非常易于使用,正如官方网站所示,从api获取数据将是:


当我不需要一个全局状态时,使用redux是不是太过分了?它仍然是最好的选择吗?@PakitoSec redux仍然是一个可靠的选择,至少对于许多逻辑文件来说,独立的业务代码,并且使用redux DevTools扩展,使用Chrome进行开发和调试很容易。使用rx.js,UI组件不会感觉到任何复杂的数据流。因此,即使是rx.js逻辑(如redux observable中的epic)也是高度可重用的。我刚刚用redux observable重构了我以前使用本地状态的项目,真是太神奇了。另一个选择是MobX,但我不太使用它,所以不能给你宝贵的经验。谢谢,我会这么做的。