【问题标题】:Call API using axios and Redux Saga always return undefined使用 axios 和 Redux Saga 调用 API 总是返回 undefined
【发布时间】:2021-09-20 06:20:48
【问题描述】:

我正在使用axios和redux saga调用api并检查本地输入,但是redux saga总是返回undefined

使用 axios 获取数据的功能

export function requestGetUser() {
  return axios({
    method: 'get',
    url: 'https://my-json-server.typicode.com/khanh21011999/demo/user',
  });
}

动作文件

export const getUser = () => ({
    type: actionList.GET_USER,
});
export const setUser = (user) => ({
    type: actionList.SET_USER,
    user,
});
export const GetUserInfo = (user, password) => {
    return{
        type: actionList.GET_USER_INFO,
        data: {user, password},
    }
};
export const LoginSuccess = (data) => {
    return {
        type: actionList.LOGIN_SUCCESS,
        data,
    };
};

export const LoginFailed = (data) => {
    return {
        type: actionList.LOGIN_FAIL,
        data,
    };
};

export const Logout = (data) => {
    return {
        type: actionList.LOG_OUT,
        data
    };
};

Redux-saga 部分

我记录了所有内容,但它返回 undefined

export function* LoginsSagaFunc() {
    yield takeLatest('GET_USER_INFO', loginSaga)

}
function* SaveToAsyncStorage(data) {
    try {
        AsyncStorage.setItem(
            'data',
            JSON.stringify({
                username: data.username,
                password: data.password
            }))
    } catch (e) {
        console.log('error save to Storage');
    }
}

function* loginSaga(action) {

    console.log('Saga is working')
    const getJson = yield call(requestGetUser)
    const getJsonData = JSON.parse(JSON.stringify(getJson))

    const getJsonUsername = String(getJsonData.username)
    console.log('JsonUsername '+getJsonUsername)
    console.log("local data " + action.data.username)
    console.log('getJsonData '+getJsonData)
    console.log('GetJson '+getJson)
    

    const getJsonPassword = String(getJsonData.password)

    if (String(action.data.username) === getJsonUsername) {
        if (String(action.data.password) === getJsonPassword) {
            console.log('saga login success')
            yield put({type: 'LOGIN_SUCCESS'})
            SaveToAsyncStorage(action.data)
        }
        else {
            console.log('saga password fail')
        }
    }
    else {
        console.log("saga user fail")
    }
}

减速器

const initStateAuth={
        isAuth:false,
        isLogIn:false
    }
    const AuthReducer =(state=initStateAuth,action)=>{
        switch (action.type) {
        case actionList.LOGIN_SUCCESS:
          {
            console.log('action : LOG IN SUCCESS');
            return {
                    
              isAuth: true,
                        isLogIn: true,
            };
          }
            case actionList.GET_USER_INFO:
                {
                    return initStateAuth
                }
        case actionList.LOGIN_FAIL:
          {
           
            return initStateAuth
          }
        case actionList.LOG_OUT:
          {
            return initStateAuth
          }
        default:
          return state;
      }
    
    
    }
    export default AuthReducer

我如何在主文件上分派

 function LoginScreen({navigation}) {
    // set timeout ID for setTimeOut()
    const timeIdRef = React.useRef(null);
    const dispatch = useDispatch();
    const [username, getUsername] = useState('');
    const [password, getPassword] = useState('')

    // handleInput = (e) => {
    //  getUserInfo(e.target.value);
    // };

    // mock user from fake api
    useEffect(() => {
        // dispatch(getUser());
        
    }, [dispatch]);
    dispatch(GetUserInfo(username, password));
    //  const handlegetdata= ({user,password})=>{
    // dispatch(GetUserInfo(user,password))
    // // }

    // console.log(handleGetdata.user)


    const user = useSelector((state) => {
        return state.User.user;
    });
    // console.log('user' + username)
    //  console.log('userJSon'+user.username)
    useEffect(() => {
        return () => {
            if (timeIdRef.current) {
                // make sure this is always cleared in case clearTo is never called
                clearTimeout(timeIdRef.current);
            }
        };
    }, [timeIdRef]);
    // console.log();

    const Login = useSelector((state) => {
        return state.LoginAction.loginStatus;
    });
    // console.log(Login)
    //   const initModal = false;
    // eslint-disable-next-line require-jsdoc
    function handleLogin() {
        dispatch({type: 'changeLogin'});
    }
    function handlDefault() {
        dispatch({type: 'getDefault'});
    }

    // not show??
    // console.log(username);
    // console.log('Login ' + Login)
    //   const [show, doShow] = useState(initModal);

    // const [visible, UpdateView] = useState(false)

    // Show modal dialog
    //   function ChangeModalValue() {
    //     console.log(show);
    //     doShow(!show);
    //   }
    // setTimer after Model Appear

    function SetTimer() {
        handleLogin();
        if (timeIdRef.current) {
            // clear any previous timeIdRef to avoid multiple button click activate multiple setTimeout
            clearTimeout(timeIdRef.current);
        }
        const timeID = setTimeout(() => {
            navigation.navigate('Home');
        }, 3000);
        timeIdRef.current = timeID;
    }

    function clearTO() {
        clearTimeout(timeIdRef.current);
        timeIdRef.current = null;
        handlDefault();
    }

    // make text black when check complete
    function getTextStyle(isValid) {
        if (isValid) {
            return {
                color: 'black',
            };
        }

        return {
            color: 'grey',
        };
    }
    //   function getLoginText() {
    //     return <CirclesLoader />;
    //   }
    // function hideText(visible){
    //     if(isDisabler)

    // }
    const loginValidationSchema = Yup.object().shape({
        email: Yup.string().email('Please enter valid email').required('Email Address is Required'),
        password: Yup.string()
            .min(8, ({min}) => `Password must be at least ${min} characters`)
            .required('Password is required'),
    });
    return (
        <View style={styles.ViewStyle}>
            <Text style={{fontSize: 40}}>Login To System</Text>

            <Formik
                validateOnMount
                validationSchema={loginValidationSchema}
                initialValues={{email: '', password: ''}}
                onSubmit={value => {
                    getUsername(value.email)
                    getPassword(value.password)
                    SetTimer()
                }}
            // () => navigation.navigate('Login')
            >
                {({handleChange, handleBlur, handleSubmit, values, errors, touched, isValid}) => (
                    <View>
                        <TextInput
                            name="email"
                            placeholder="Email Address"
                            style={styles.TextInputForm}
                            onChangeText={handleChange('email')}
                            onBlur={handleBlur('email')}
                            value={values.email}
                            keyboardType="email-address"
                        />
                        {errors.email && touched.email && <Text style={styles.errorText}>{errors.email}</Text>}
                        <TextInput
                            name="password"
                            placeholder="Password"
                            onChangeText={handleChange('password')}
                            onBlur={handleBlur('password')}
                            value={values.password}
                            secureTextEntry
                            style={styles.TextInputForm}
                        />
                        {errors.password && touched.password && (
                            <Text style={styles.errorText}>{errors.password}</Text>
                        )}

                        <TouchableOpacity
                            onPress={handleSubmit}
                            style={styles.ButtonLogin}
                            disabled={!isValid || values.email === ''}>
                            {/* <CirclesLoader size={20} dotRadius={7} /> */}
                            <Text style={getTextStyle(isValid)}>Login</Text>
                        </TouchableOpacity>
                        <View>
                            <Modal transparent visible={Login}>
                                <View
                                    style={{
                                        backgroundColor: '#000000',
                                        flex: 1,
                                        justifyContent: 'center',
                                        alignContent: 'center',
                                    }}>
                                    <View style={styles.ModalStyle}>
                                        <CirclesLoader />
                                        <TextLoader
                                            textStyle={{
                                                fontSize: 25,
                                                marginTop: 20,
                                            }}
                                            text="Logging you in"
                                        />
                                        <TouchableOpacity onPress={clearTO} style={styles.ButtonBack}>
                                            <Text>Go back</Text>
                                        </TouchableOpacity>
                                    </View>
                                </View>
                            </Modal>
                        </View>
                    </View>
                )}
            </Formik>
        </View>
    );
}

另外,当我按下时,动作会获取数据,但它在 redux-saga 部分返回未定义,所以用户名总是如此相等,发生了什么??

为什么数据显示在 redux 调试器中,但我在 saga 上看不到,为什么我从 axios 获取的数据返回未定义?

一个简短的 gif 来显示发生了什么

请帮忙,万分感谢

完整代码:https://codesandbox.io/s/github/khanh21011999/Trainning-react-native

【问题讨论】:

    标签: reactjs react-native redux react-redux redux-saga


    【解决方案1】:

    首先,您的沙盒无法正常工作,因此请确保它适用于所有人。 第二次尝试在您的代码中像这样使用 async/await,我无法测试它,因为您的沙箱正在崩溃。 export async function requestGetUser() { return await axios.get('https://my-json-server.typicode.com/khanh21011999/demo/user'); }

    【讨论】:

    • 对不起,我使用的是 react-native,所以只显示代码,但我不知道如何让它们在 web 上运行 :((
    • 我试过你的方法,但它显示如下ibb.co/d2BtSQg
    • 当您显式调用get 时,为什么要使用方法和 url 属性传递对象,只需按照我在答案中写的方式尝试 axios。
    • 或者你可以简单地忽略 async/await 并返回 then 和 catch 块。
    • 我想我会选择更简单的方法,我刚刚开始学习 react 和 react native 3 周,没有网络背景,这对我来说太重要了,但感谢你的回答,我很感激那
    【解决方案2】:

    在 axios API 调用中,您需要对成功或失败的响应进行编码,如下所示:

    export function requestGetUser() {
      return axios({
        method: 'get',
        url: 'https://my-json-server.typicode.com/khanh21011999/demo/user',
      })
    .done (function(data) {
        //Get your data here upon successful fetch
      })
    .fail (function() {
        console.log("Failed to fetch data");
      })
    .always (function() {
        console.log("This function always executes whether success or fail");
      });
    }
    

    【讨论】:

    • 谢谢你的回答,但是我如何获取function(data)中的数据,我的意思是,如何访问redux-saga文件中的这些数据,我很新,请举个例子
    • 如何获取这些数据? return data 合法吗?
    • 但是当我使用console.log ibb.co/r5xFjF3时显示成功
    • 请帮忙,我被这个问题困扰了好几天:((
    • 嗨庆。您应该通过在函数内部编码从 .done 函数本身获取数据。您可以在那里执行 console.log(data) 来查看数据(data[0].fieldname、data[1].fieldname...等)。还有其他方法,例如将数据放入全局变量中,这不是一个好方法。