【问题标题】:Redux + Hooks useDispatch() in useEffect calling action twiceRedux + Hooks useDispatch() 在 useEffect 调用动作两次
【发布时间】:2020-09-13 11:29:14
【问题描述】:

我是 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);

值未定义

【问题讨论】:

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


    【解决方案1】:

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

    根据react docs,为了detect unexpected sideEffects,react 调用了某个函数两次,例如

    Functions passed to useState, useMemo, or useReducer
    

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

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

    要回答这个问题,您必须知道 values 不是来自 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);
    

    【讨论】:

    • 谢谢。但是有什么方法可以从 userProfileForm 接收我的组件中的data 吗?还是我只需要在组件中使用useState()
    • 我更新了关于如何接收组件中数据的答案。双重调用也是因为 React.StrictMode 在层次结构中的某处。使用钩子或类组件都没有关系。但是,您应该担心双重调用。如果有的话,它将帮助您找出应用程序中的问题。另请阅读有关此的文档以获取更多详细信息。我已经在帖子中添加了链接
    • 我已经尝试过const { values, handleChange, handleSubmit, data } = useProfileForm(submitForm);,但我仍然以undefined 的形式获取值和数据
    • 是的,很抱歉在传递对象时出现了一些错误。现在它的工作。谢谢
    猜你喜欢
    • 2021-01-04
    • 1970-01-01
    • 2020-05-25
    • 2020-04-08
    • 2020-01-26
    • 2023-04-03
    • 2020-06-22
    • 1970-01-01
    相关资源
    最近更新 更多