Redux +挂接useDispatch()in useEffect调用操作两次

0ejtzxu1  于 2023-03-02  发布在  其他
关注(0)|答案(2)|浏览(172)

我是redux和hooks的初学者,我正在处理表单,尝试通过useDispatch hooks调用一个action,但是它调用了我的action两次。
我指的是this文章。
下面是一个例子:

    • 使用配置文件表单. 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);
  • 未定义值 *

dnph8jn4

dnph8jn41#

动作的两次分派可能是因为您在react层次结构中使用了React.StrictMode
根据react文档,为了detect unexpected sideEffects,react会两次调用某些函数,例如

Functions passed to useState, useMemo, or useReducer

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

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

要回答这个问题,您必须知道values不是reducer的dispatch操作响应的结果,而是在调用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);
hwamh0ep

hwamh0ep2#

有两种方法可以克服这个问题。我已经研究了这个问题,并找到了一个解决方案,从react中的根文件中删除React.StrictMode,如

root.render(
   <React.StrictMode>
      <App />
   </React.StrictMode>
);

喜欢这个

root.render(<App />);

但是React.StrictMode模式非常适合捕捉代码中的警告和问题。所以我找到了另一种方法来解决这个问题,我们必须在useEffect中使用异步调用来避免双重API调用。所以我在useEffect中使用api调用,如下所示

useEffect(()=>{
    let getData = setTimeout(() => {
        dispatch(fetchProfile());
      }, 0)
    return () => clearTimeout(getData)
  },[])

也可以使用promise创建异步调用

相关问题