Redux + Hook在useEffect调用动作中两次使用useDispatch()

时间:2020-05-26 15:37:25

标签: javascript reactjs redux react-redux react-hooks

我是redux和hooks的初学者。我正在处理表单,并尝试通过useDispatch挂钩调用操作,但是它两次调用了我的操作。

我指的是this文章。

这里是示例:

useProfileForm.js

import { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchProfile } from '../../../redux/profile/profile.actions';

const useProfileForm = (callback) => {

    const profileData = useSelector(state =>
        state.profile.items        
    );

    let data;
    if (profileData.profile) {
        data = profileData.profile;
    }

    const [values, setValues] = useState(data);

    const dispatch = useDispatch();

    useEffect(() => {
        dispatch(fetchProfile());
    }, [dispatch]);

    const handleSubmit = (event) => {
        if (event) {
            event.preventDefault();
        }
        callback();
    };

    const handleChange = (event) => {
        event.persist();
        setValues(values => ({ ...values, [event.target.name]: event.target.value }));
    };

    return {
        handleChange,
        handleSubmit,
        values,
    }
};

export default useProfileForm;

操作

export const FETCH_PROFILE_BEGIN = "FETCH_PROFILE_BEGIN";
export const FETCH_PROFILE_SUCCESS = "FETCH_PROFILE_SUCCESS";
export const FETCH_PROFILE_FAILURE = "FETCH_PROFILE_FAILURE";
export const ADD_PROFILE_DETAILS = "ADD_PROFILE_DETAILS";

function handleErrors(response) {
    if (!response.ok) {
        throw Error(response.statusText);
    }
    return response;
}

function getProfile() {
    return fetch("url")
        .then(handleErrors)
        .then(res => res.json());
}

export function fetchProfile() {
    return dispatch => {
        dispatch(fetchProfileBegin());
        return getProfile().then(json => {
            dispatch(fetchProfileSuccess(json));
            return json;
        }).catch(error =>
            dispatch(fetchProfileFailure(error))
        );
    };
}

export const fetchProfileBegin = () => ({
    type: FETCH_PROFILE_BEGIN
});

export const fetchProfileSuccess = profile => {
    return {
        type: FETCH_PROFILE_SUCCESS,
        payload: { profile }
    }
};

export const fetchProfileFailure = error => ({
    type: FETCH_PROFILE_FAILURE,
    payload: { error }
});

export const addProfileDetails = details => {
    return {
        type: ADD_PROFILE_DETAILS,
        payload: details
    }
};

减速器:

    import { ADD_PROFILE_DETAILS, FETCH_PROFILE_BEGIN, FETCH_PROFILE_FAILURE, FETCH_PROFILE_SUCCESS } from './profile.actions';

    const INITIAL_STATE = {
        items: [],
        loading: false,
        error: null
    };

    const profileReducer = (state = INITIAL_STATE, action) => {
        switch (action.type) {
            case ADD_PROFILE_DETAILS:
                return {
                    ...state,
                    addProfileDetails: action.payload
                }
            case FETCH_PROFILE_BEGIN:
                return {
                    ...state,
                    loading: true,
                    error: null
                };

            case FETCH_PROFILE_SUCCESS:
                return {
                    ...state,
                    loading: false,
                    items: action.payload.profile
                };

            case FETCH_PROFILE_FAILURE:
                return {
                    ...state,
                    loading: false,
                    error: action.payload.error,
                    items: []
                };
            default:
                return state;
        }
    }

    export default profileReducer;


**Component:**

import React from 'react';
import { connect } from 'react-redux';
import useProfileForm from './useProfileForm';
import { addProfileDetails } from '../../../redux/profile/profile.actions';

const EducationalDetails = () => {

    const { values, handleChange, handleSubmit } = useProfileForm(submitForm);

    console.log("values", values);


    function submitForm() {
        addProfileDetails(values);
    }

    if (values) {
        if (values.error) {
            return <div>Error! {values.error.message}</div>;
        }

        if (values.loading) {
            return <div>Loading...</div>;
        }
    }

    return (
        <Card>
            ...some big html
        </Card>
    )
}

const mapDispatchToProps = dispatch => ({
    addProfileDetails: details => dispatch(details)
});

export default connect(null, mapDispatchToProps)(EducationalDetails);

此外,当我将数据从const [values, setValues] = useState(data); useState传递到值时,理想情况下,我应该在组件中接收该值,但由于显示为未定义,所以我没有得到它。

const { values, handleChange, handleSubmit } = useProfileForm(submitForm);

值未定义

enter image description here

enter image description here

1 个答案:

答案 0 :(得分:1)

两次分派动作可能是因为您在反应层次结构中使用了React.StrictMode

根据react文档,为了detect unexpected sideEffects,react两次调用某些函数,例如

Functions passed to useState, useMemo, or useReducer

现在,由于react-redux是在react API之上实现的,因此动作实际上被调用了两次

也当我从const [values,setValues] = useState(data)传递数据时;将useState设置为值,那么理想情况下,我应该在组件中接收该值,但是由于显示未定义,我没有得到它。

要回答此问题,您必须知道值不是来自reducer的调度动作的响应而来的结果,而是在调用handleChange时更新的状态,因此应该不受该动作的影响

我认为您的意思是公开useProfileForm中遗忘的redux数据

const useProfileForm = (callback) => {

    const profileData = useSelector(state =>
        state.profile.items        
    );

    let data;
    if (profileData.profile) {
        data = profileData.profile;
    }

    const [values, setValues] = useState(data);

    const dispatch = useDispatch();

    useEffect(() => {
        dispatch(fetchProfile());
    }, [dispatch]);

    const handleSubmit = (event) => {
        if (event) {
            event.preventDefault();
        }
        callback();
    };

    const handleChange = (event) => {
        event.persist();
        setValues(values => ({ ...values, [event.target.name]: event.target.value }));
    };

    return {
        handleChange,
        handleSubmit,
        values,
        data // This is the data coming from redux store on FetchProfile and needs to logged
    }
};

export default useProfileForm;

您可以使用组件中的数据,例如

const { values, handleChange, handleSubmit, data } = useProfileForm(submitForm);