#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
- [Просмотр] Ваше представление реагирует по-разному.
- [Действия] Мы создаем абстракцию над этими реакциями, называемую «действия».
- [Диспетчер] Эти действия отправляются в хранилище.
- [Редуктор] В хранилище у нас есть логика, записанная в «редукторах», которые просматривают действия и выполняют манипуляции с хранилищем для обновления состояния хранилища.
- [Состояние] Это состояние загружается в дерево состояний 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