Когда будет использоваться bindActionCreators в реакции/сокращении?
Документы Redux для bindActionCreators гласят:
Единственный вариант использования bindActionCreators
- это когда вы хотите передать некоторых создателей действий в компонент, который не знает о Redux, и вы не хотите передавать ему диспетчеризацию или хранилище Redux.
Каков будет пример, где bindActionCreators
будет использоваться/нужен?
Какой компонент не будет знать о Redux?
Каковы преимущества/недостатки обоих вариантов?
//actionCreator
import * as actionCreators from './actionCreators'
function mapStateToProps(state) {
return {
posts: state.posts,
comments: state.comments
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch)
}
против
function mapStateToProps(state) {
return {
posts: state.posts,
comments: state.comments
}
}
function mapDispatchToProps(dispatch) {
return {
someCallback: (postId, index) => {
dispatch({
type: 'REMOVE_COMMENT',
postId,
index
})
}
}
}
Ответы
Ответ 1
99% времени, он используется с функцией React-Redux connect()
, как часть параметра mapDispatchToProps
. Он может использоваться явно внутри предоставляемой вами функции mapDispatch
или автоматически, если вы используете синтаксис сокращенного объекта и передаете объект, полный создателей действия, в connect
.
Идея заключается в том, что, предварительно привязывая создателей действия, компонент, который вы передаете connect()
технически "не знает", что он подключен, - он просто знает, что ему нужно запустить this.props.someCallback()
. С другой стороны, если вы не связывали создателей действий и вызывали this.props.dispatch(someActionCreator())
, теперь компонент "знает", что он связан, потому что он ожидает существования props.dispatch
.
Я написал некоторые мысли по этой теме в своем сообщении в блоге Idiomatic Redux: зачем использовать создателей действий?.
Ответ 2
Я не думаю, что самый популярный ответ, на самом деле адресует вопрос.
Все приведенные ниже примеры, по сути, делают одно и то же и следуют принципу "без предварительной привязки".
// option 1
const mapDispatchToProps = (dispatch) => ({
action: () => dispatch(action())
})
// option 2
const mapDispatchToProps = (dispatch) => ({
action: bindActionCreators(action, dispatch)
})
// option 3
const mapDispatchToProps = {
action: action
}
Вариант #3
является просто сокращением для варианта #1
, поэтому реальный вопрос, почему можно использовать вариант #1
против варианта #2
. Я видел, как они оба использовались в коде Resase-Redux, и я нахожу это довольно запутанным.
Я думаю, что путаница происходит из того факта, что все примеры в react-redux
документ использует bindActionCreators
в то время как документ для bindActionCreators (как указано в самом вопросе) говорит, чтобы не использовать его с среагировать-Redux.
Я предполагаю, что ответ заключается в согласованности в кодовой базе, но я лично предпочитаю явно оборачивать действия в диспетчере всякий раз, когда это необходимо.
Ответ 3
Более полный пример, передайте объект, полный создателей действия для подключения:
import * as ProductActions from './ProductActions';
// component part
export function Product({ name, description }) {
return <div>
<button onClick={this.props.addProduct}>Add a product</button>
</div>
}
// container part
function mapStateToProps(state) {
return {...state};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
...ProductActions,
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Product);
Ответ 4
Я постараюсь ответить на оригинальные вопросы...
Smart & Dumb компоненты
В своем первом вопросе вы в основном спрашиваете, зачем в первую очередь нужен bindActionCreators
и какие компоненты не должны знать о Redux.
Короче говоря, идея заключается в том, что компоненты должны быть разделены на умные (контейнерные) и тупые (презентационные) компоненты. Тупые компоненты работают по мере необходимости. Их работа состоит в том, чтобы преобразовывать данные в HTML и ничего более. Они не должны знать о внутренней работе приложения. Их можно рассматривать как глубокий передний слой кожи вашего приложения.
С другой стороны, интеллектуальные компоненты являются своего рода клеем, который подготавливает данные для немых компонентов и предпочтительно не выполняет рендеринг HTML.
Такая архитектура способствует слабой связи между уровнем пользовательского интерфейса и уровнем данных под ним. Это, в свою очередь, позволяет легко заменить любой из двух слоев чем-то другим (например, новым дизайном пользовательского интерфейса), что не сломает другой слой.
Чтобы ответить на ваш вопрос: глупые компоненты не должны знать о Redux (или о каких-либо ненужных деталях реализации уровня данных в этом отношении), потому что мы могли бы захотеть заменить его чем-то другим в будущем.
Вы можете узнать больше об этой концепции в руководстве Redux, а также в статье Презентационные и Контейнерные компоненты Дана Абрамова.
Какой пример лучше
Второй вопрос касался преимуществ/недостатков приведенных примеров.
В первом примере создатели действий определены в отдельном файле/модуле actionCreators
, что означает, что они могут быть использованы в другом месте. Это в значительной степени стандартный способ определения действий. Я действительно не вижу никаких недостатков в этом.
Второй пример определяет создателей действий, которые имеют ряд недостатков:
- создатели действий не могут быть повторно использованы (очевидно)
- вещь более многословна, что означает менее читабельный
- Типы действий жестко запрограммированы - желательно определять их как
consts
отдельно, чтобы на них можно было ссылаться в редукторах - это уменьшило бы вероятность опечаток - определение встроенных действий создателей противоречит рекомендованному/ожидаемому способу их использования, что сделает ваш код менее читабельным для сообщества, если вы планируете поделиться своим кодом
Второй пример имеет одно преимущество перед первым - это быстрее писать! Так что если у вас нет больших планов для вашего кода, это может быть просто хорошо.
Надеюсь, мне удалось кое-что прояснить...
Ответ 5
используя bindActionCreators
, он может сгруппировать несколько функций действия и передать их компоненту, который не знает о Redux (тупой компонент), как это так
// actions.js
export const increment = () => ({
type: 'INCREMENT'
})
export const decrement = () => ({
type: 'DECREMENT'
})
// main.js
import { Component } from 'react'
import { bindActionCreators } from 'redux'
import * as Actions from './actions.js'
import Counter from './counter.js'
class Main extends Component {
constructor(props) {
super(props);
const { dispatch } = props;
this.boundActionCreators = bindActionCreators(Actions, dispatch)
}
render() {
return (
<Counter {...this.boundActionCreators} />
)
}
}
// counter.js
import { Component } from 'react'
export default Counter extends Component {
render() {
<div>
<button onclick={() => this.props.increment()}
<button onclick={() => this.props.decrement()}
</div>
}
}
Ответ 6
Один хороший пример использования для bindActionCreators
- для интеграции с redux-saga с использованием redux-saga-рутин. Например:
// routines.js
import { createRoutine } from "redux-saga-routines";
export const fetchPosts = createRoutine("FETCH_POSTS");
// Posts.js
import React from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { fetchPosts } from "routines";
class Posts extends React.Component {
componentDidMount() {
const { fetchPosts } = this.props;
fetchPosts();
}
render() {
const { posts } = this.props;
return (
<ul>
{posts.map((post, i) => (
<li key={i}>{post}</li>
))}
</ul>
);
}
}
const mapStateToProps = ({ posts }) => ({ posts });
const mapDispatchToProps = dispatch => ({
...bindActionCreators({ fetchPosts }, dispatch)
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Posts);
// reducers.js
import { fetchPosts } from "routines";
const initialState = [];
export const posts = (state = initialState, { type, payload }) => {
switch (type) {
case fetchPosts.SUCCESS:
return payload.data;
default:
return state;
}
};
// api.js
import axios from "axios";
export const JSON_OPTS = { headers: { Accept: "application/json" } };
export const GET = (url, opts) =>
axios.get(url, opts).then(({ data, headers }) => ({ data, headers }));
// sagas.js
import { GET, JSON_OPTS } from "api";
import { fetchPosts } from "routines";
import { call, put, takeLatest } from "redux-saga/effects";
export function* fetchPostsSaga() {
try {
yield put(fetchPosts.request());
const { data } = yield call(GET, "/api/posts", JSON_OPTS);
yield put(fetchPosts.success(data));
} catch (error) {
if (error.response) {
const { status, data } = error.response;
yield put(fetchPosts.failure({ status, data }));
} else {
yield put(fetchPosts.failure(error.message));
}
} finally {
yield put(fetchPosts.fulfill());
}
}
export function* fetchPostsRequestSaga() {
yield takeLatest(fetchPosts.TRIGGER, fetchPostsSaga);
}
Обратите внимание, что этот шаблон может быть реализован с использованием React Hooks (начиная с React 16.8).
Ответ 7
Я также хотел узнать больше о bindActionsCreators, и вот как я реализовал свой проект.
// Actions.js
// Action Creator
const loginRequest = (username, password) => {
return {
type: 'LOGIN_REQUEST',
username,
password,
}
}
const logoutRequest = () => {
return {
type: 'LOGOUT_REQUEST'
}
}
export default { loginRequest, logoutRequest };
В вашем React компоненте
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import ActionCreators from './actions'
class App extends Component {
componentDidMount() {
// now you can access your action creators from props.
this.props.loginRequest('username', 'password');
}
render() {
return null;
}
}
const mapStateToProps = () => null;
const mapDispatchToProps = dispatch => ({ ...bindActionCreators(ActionCreators, dispatch) });
export default connect(
mapStateToProps,
mapDispatchToProps,
)(App);
Ответ 8
Одним из возможных применений bindActionCreators()
является "сопоставление" нескольких действий вместе в виде одной пропоры.
Обычная отправка выглядит так:
Сопоставьте пару общих действий пользователя с реквизитом.
const mapStateToProps = (state: IAppState) => {
return {
// map state here
}
}
const mapDispatchToProps = (dispatch: Dispatch) => {
return {
userLogin: () => {
dispatch(login());
},
userEditEmail: () => {
dispatch(editEmail());
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
В более крупных проектах составление карт каждой отправки в отдельности может показаться громоздким. Если у нас есть куча действий, которые связаны друг с другом, мы можем объединить эти действия. Например, файл действий пользователя, который выполнял все виды различных действий, связанных с пользователем. Вместо того, чтобы вызывать каждое действие как отдельную отправку, мы можем использовать bindActionCreators()
вместо dispatch
.
Несколько рассылок с использованием bindActionCreators()
Импортируйте все ваши связанные действия. Скорее всего, они находятся в одном и том же файле в магазине приставок.
import * as allUserActions from "./store/actions/user";
И теперь вместо использования диспетчеризации используйте bindActionCreators()
const mapDispatchToProps = (dispatch: Dispatch) => {
return {
...bindActionCreators(allUserActions, dispatch);
},
};
};
export default connect(mapStateToProps, mapDispatchToProps,
(stateProps, dispatchProps, ownProps) => {
return {
...stateProps,
userAction: dispatchProps
ownProps,
}
})(MyComponent);
Теперь я могу использовать userAction
для вызова всех действий в вашем компоненте.
IE: userAction.login()
userAction.editEmail()
или this.props.userAction.login()
this.props.userAction.editEmail()
.
ПРИМЕЧАНИЕ. Вам не нужно сопоставлять bindActionCreators() с одним реквизитом. (Дополнительный => {return {}}
который отображается на userAction
). Вы также можете использовать bindActionCreators()
чтобы отобразить все действия одного файла как отдельные объекты. Но я считаю, что это может сбивать с толку. Я предпочитаю, чтобы каждому действию или "группе действий" давалось явное имя. Мне также нравится называть ownProps
более подробно о том, что это за "дочерние реквизиты" или откуда они ownProps
. При использовании Redux + React может возникнуть некоторая путаница в отношении того, где поставляются все реквизиты, поэтому, чем больше описаний, тем лучше.
Ответ 9
Заявление документов очень ясно:
Единственный вариант использования bindActionCreators
- это когда вы хотите передать некоторых создателей действий в компонент, который не знает о Redux, и вы не хотите передавать ему диспетчеризацию или хранилище Redux.
Это, безусловно, вариант использования, который может возникнуть в следующих и только одно условие:
Допустим, у нас есть компоненты A и B:
// A use connect and updates the redux store
const A = props => {}
export default connect()(A)
// B doesn't use connect therefore it does not know about the redux store.
const B = props => {}
export default B
Ввести в реакцию-редукс: (A)
const boundActionCreators = bindActionCreators(SomeActionCreator, dispatch)
// myActionCreatorMethod,
// myActionCreatorMethod2,
// myActionCreatorMethod3,
// when we want to dispatch
const action = SomeActionCreator.myActionCreatorMethod('My updates')
dispatch(action)
Вводится с помощью реакции-редукса: (B)
const { myActionCreatorMethod } = props
<B myActionCreatorMethod={myActionCreatorMethod} {...boundActionCreators} />
Заметили следующее?
-
Мы обновили хранилище избыточности через компонент А, пока мы не знали о хранилище избыточности в компоненте В.
-
Мы не обновляем компонент А. Чтобы узнать, что именно я имею в виду, вы можете изучить этот пост. Я надеюсь, что у вас есть идея.