Вызов действия в другом действии в Redux React

#reactjs #redux

#reactjs #redux

Вопрос:

Я изучаю Redux в React. Я использую redux-thunk. У меня есть действие, подобное приведенному ниже

 export const login = (value, history) => dispatch => {
    Axios.post('/api/users/login', value)
        .then(response => {
            dispatch(getAddress()); // I am calling like this
        })
        .catch(error => {});
}
  

Мое действие с адресом выглядит следующим образом

 export const getAddress = () => dispatch => {
    Axios.get('/api/address')
        .then(response => {
            dispatch({
                type: 'getAddresses',
                payload: response.data
            });
        })
        .catch(function (error) {});
}
  

Я пытаюсь отобразить значение в моем компоненте, как показано ниже

     render() {
        return (
           <div>Hello {console.log(this.props)}</div> // I am getting empty object
        );
    }


const mapStateToProps = state => ({
  addresses: state.address
});

export default connect(mapStateToProps)(Dashboard);
  

Но я получаю пустой объект.

Я хотел бы перенаправить пользователя на панель мониторинга после входа в систему, где будут отображаться адреса. Как я могу это сделать?

Комментарии:

1. Я не уверен, что нарушенные цепочки обещаний вызывают здесь проблемы, поскольку есть только 2 действия, и вы не используете результаты первого действия во втором, я бы ожидал, что они оба будут отправлены. Проблема может заключаться в том, что ответы не соответствуют вашим ожиданиям, или были ошибки, которые были подавлены с помощью catch . В любом случае, рекомендуется соблюдать цепочки обещаний.

2. Есть ли у вас редуктор, который обрабатывает ответ API и сохраняет его как состояние?

Ответ №1:

Промежуточное программное обеспечение Redux thunk позволяет связываться через dispatch , dispatch(...) возвращает отправленное действие, т.е. обещание, если цепочки обещаний не были нарушены:

 export const login = (value, history) => dispatch => {
    return Axios.post('/api/users/login', value) // return!
    .then(response => {
        return dispatch(getAddress()); // return!
    })
    .catch(error => {});
}

export const getAddress = () => dispatch => {
    return Axios.get('/api/address') // return!
    .then(response => {
        return dispatch({ // return!
            type: 'getAddresses',
            payload: response.data
        });
    })
    .catch(function (error) {});
}
  

Первоначальное действие должно быть куда-то отправлено. В настройке vanilla Redux оно может быть отправлено после определения хранилища:

 store.dispatch(login());
  

При настройке React Redux компоненты являются неотъемлемыми частями потока данных Redux. Асинхронный сбой — это побочный эффект, и он должен быть отправлен один раз в componentDidMount . В случае первоначального действия, оно должно быть отправлено в родительском компоненте:

 @connect()
class App extends Component {
  componentDidMount() {
    return this.props.dispatch(login());
  }

  render() {
    return !this.props.addresses ? 'Loading' :  <Dashboard />
  }
}
  

Вот демонстрация.

Комментарии:

1. Спасибо @estus за ваш ответ. Но ваше решение не работает. Я видел вашу демонстрацию. Вы использовали componentDidMount() { return this.props.dispatch(login()); } Я бы не хотел этого делать. Я хотел бы перенаправить пользователя на панель мониторинга после входа в систему, где будут отображаться адреса. Как я могу это сделать?

2. Я бы не хотел этого делать — в этом-то и проблема. Конечно, вам нужно это сделать. Я не в курсе ваших компонентов, но это можно сделать в компоненте, который является родительским для dashboard.

3. Спасибо @estus. Не могли бы вы, пожалуйста, сказать, как я могу получить address данные в моем dashboard компоненте, используя ваше решение? Я хотел бы перенаправить пользователя на панель мониторинга после входа в систему, где будут отображаться адреса.

4. Вы можете получить это так, как вы изначально это делали, и это было показано в демонстрации. Вы получаете его из хранилища с помощью mapStateToProps.

Ответ №2:

Итак, для этого требуется очень простое введение в поток Redux с «побочными эффектами».

Поток Redux

  1. [Просмотр] Ваше представление реагирует по-разному.
  2. [Действия] Мы создаем абстракцию над этими реакциями, называемую «действия».
  3. [Диспетчер] Эти действия отправляются в хранилище.
  4. [Редуктор] В хранилище у нас есть логика, записанная в «редукторах», которые просматривают действия и выполняют манипуляции с хранилищем для обновления состояния хранилища.
  5. [Состояние] Это состояние загружается в дерево состояний react.

Предупреждение об определении! В Redux такие вещи, как вызовы API, называются побочными эффектами. Это потому, что вы делаете что-то за пределами обычного однонаправленного потока данных.

введите описание изображения здесь

Надеюсь, вы поняли этот поток. Меня особенно беспокоит конкретная часть, которая, похоже, вас смущает. Итак, в вашем коде вы пытались это сделать.

  then(response => {
     return dispatch(getAddress()); 
 })
  

Что ваш код пытается здесь сделать, так это то, что он пытается «отправить» побочный эффект. Это неправильно!

Отправка — это почти всегда простой старый JS-объект, который является сериализуемым. Обычно у вас будет стереотипная структура, подобная этой. Которое, похоже, у вас есть в вашем другом вызове get address.

 { 
    type: 'GET_ADDRESS_REQUEST_SUCCESS', 
    payload: {} 
}
  

Итак, правильный подход к вашей задаче выглядит следующим образом. Подумайте об отправке действий, таких как отправка сигналов, когда происходят различные вещи.

 export const login = (value, history) => dispatch => {
    Axios.post('/api/users/login', value)
        .then(response => {
            //Send out a success signal
            dispatch({
                type: 'loginSuccess',
                payload: response.data
            });

            //Now that I have logged in successfully I can call for my address
            //This is simply how you call an action within an action!
            getAddress();
        }).catch(error => {
            //login failed signal
            dispatch({
                type: 'loginFailed',
                payload: response
            }); // I am calling like this
        });
}

export const getAddress = () => dispatch => {
    Axios.get('/api/address')
        .then(response => {
            //Get address success signal
            dispatch({
                type: 'getAddressSuccess',
                payload: response.data
            });
        }).catch(function (error) {
            //Get addresses failed signal
            dispatch({
                type: 'getAddressFailed',
                payload: error
            }); 
        });
}
  

Следующая важная часть — это reducer, который вы здесь не включили! Если actions выдает сигналы, думайте о редукторах как о приемнике, который принимает и обрабатывает этот сигнал.
Редуктор обновит существующее состояние на основе развернутого действия.

 export default function appReducer(state, action) {
    switch (action.type) {
        case 'getAddressSuccess': //Waiting on the signal!
            return Object.assign({},state,{address : action.payload} 
            //Set the proper address based on your payload from the api call

        default:
            return state

    }
}
  

Вы также должны правильно настроить свою конфигурацию redux thunk, чтобы это полностью сработало.
Приветствия!

============ РЕДАКТИРОВАТЬ=============

Итак, после проверки кодовой базы вы использовали объединяющий редуктор для объединения нескольких редукторов вместе.

 import { combineReducers } from 'redux';
import authReducer from './authReducer';
import addressReducer from './addressReducer';

const rootReducer = combineReducers({
  authReducer,
  addressReducer
});

export default rootReducer;
  

Итак, в mapsStateToProps состояние вашего хранилища будет выглядеть следующим образом

 {
  authReducer: {
    // state managed by the authReducer 
  },
  addressReducer: {
    address: 'Test Streat, London'
  }
}
  

Поэтому измените ваши mapStateToProps, чтобы они выглядели следующим образом

 const mapStateToProps = state => ({
  addresses: state.addressReducer.address
});

export default connect(mapStateToProps)(Dashboard);
  

Комментарии:

1. Спасибо @jdc за ваш длинный и описательный ответ. Но ваше решение не работает. Я хотел бы перенаправить пользователя на панель мониторинга после входа в систему, где будут отображаться адреса. Как я могу это сделать?

2. Какая часть не работает? Также используете ли вы react router?

3. Я не думаю, что это часть первоначального вопроса, который вы задали, который заключался в том, как вызвать действие с помощью in action. Я предполагаю, что вы неправильно настроили свои интеграции redux. Сначала вы должны следовать руководству о том, как интегрировать react и redux.

4. Как вы пришли к выводу, что ваша настройка redux верна? Нужно будет посмотреть на общую кодовую базу, чтобы понять это.

5. Я обновил исправление! Это должно быть проблемой. @abuabu