В документации 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
})
}
}
}
источник
#3
является сокращением для варианта#1
?mapDispatchToProps
:function
,object
и пропавших без вести. Здесьredux
иconnect
иaddDispatchToProps
для простого компонента кажется излишним, если я могу просто передать одну опору. Но, насколько мне известно, почти все случаи дляmapDispatch
реквизита будут либо вариантами,#1
либо#3
упомянутыми в ответеВ 99% случаев он используется с
connect()
функцией React-Redux как частьmapDispatchToProps
параметра. Его можно использовать явно внутриmapDispatch
предоставляемой вами функции или автоматически, если вы используете сокращенный синтаксис объекта и передаете объект, полный создателей действийconnect
.Идея состоит в том, что путем предварительной привязки создателей действий компонент,
connect()
которому вы передаете технически, «не знает», что он подключен - он просто знает, что его нужно запуститьthis.props.someCallback()
. С другой стороны, если вы не связали создателей действий и не вызвали ихthis.props.dispatch(someActionCreator())
, теперь компонент «знает», что он подключен, потому что он ожидаетprops.dispatch
существования.Я написал несколько мыслей по этой теме в своем сообщении в блоге. Idiomatic Redux: зачем использовать создатели действий? .
источник
Более полный пример, передайте объект, полный создателей действий для подключения:
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);
источник
Постараюсь ответить на оригинальные вопросы ...
Умные и глупые компоненты
В первом вопросе вы в основном спрашиваете, зачем он
bindActionCreators
вообще нужен и какие компоненты не должны знать о Redux.Короче говоря, идея состоит в том, что компоненты должны быть разделены на интеллектуальные (контейнерные) и глупые (презентационные) компоненты. Глупые компоненты работают по принципу служебной необходимости. Их работа по душе - преобразовывать данные в HTML и не более того. Они не должны знать о внутренней работе приложения. Их можно рассматривать как глубокий передний слой кожи вашего приложения.
С другой стороны, интеллектуальные компоненты - это своего рода клей, который подготавливает данные для немых компонентов и предпочтительно не выполняет рендеринг HTML.
Такая архитектура способствует слабой связи между уровнем пользовательского интерфейса и уровнем данных под ним. Это, в свою очередь, позволяет легко заменить любой из двух слоев чем-то другим (например, новым дизайном пользовательского интерфейса), что не нарушит работу другого уровня.
Чтобы ответить на ваш вопрос: глупые компоненты не должны знать о Redux (или о любых ненужных деталях реализации уровня данных, если на то пошло), потому что мы, возможно, захотим заменить его чем-то другим в будущем.
Вы можете найти больше об этой концепции в руководстве Redux и более подробно в статье « Компоненты представления и контейнера » Дэна Абрамова.
Какой пример лучше
Второй вопрос касался достоинств / недостатков приведенных примеров.
В первом примере создатели действий определены в отдельном
actionCreators
файле / модуле, что означает, что их можно повторно использовать в другом месте. Это в значительной степени стандартный способ определения действий. Минусов в этом особо не вижу.Во втором примере определяются встроенные создатели действий, у которых есть несколько недостатков:
consts
отдельно, чтобы на них можно было ссылаться в редукторах - это снизит вероятность ошибок при вводеУ второго примера есть одно преимущество перед первым - писать быстрее! Так что, если у вас нет больших планов относительно вашего кода, все может быть хорошо.
Надеюсь, мне удалось немного прояснить ситуацию ...
источник
Одно из возможных применений
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 ()
Импортируйте все связанные с вами действия. Скорее всего, все они находятся в одном файле в магазине redux.
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
чтобы более подробно описать, что это за «дочерний реквизит» и откуда они берутся. При использовании Redux + React может немного запутаться, где предоставляются все реквизиты, поэтому чем более наглядно, тем лучше.источник
используя
bindActionCreators
, он может группировать несколько функций действий и передавать их компоненту, который не знает Redux (Dumb Component), например// 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> } }
источник
Я также хотел узнать больше о 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);
источник
Один из хороших вариантов использования
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).
источник
Документы заявление очень ясно:
Очевидно, что это вариант использования, который может возникнуть при следующем и только одном условии:
Допустим, у нас есть компоненты 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} />
Заметили следующее?
Мы обновили хранилище redux с помощью компонента A, пока нам не было известно о хранилище redux в компоненте B.
Мы не обновляем компонент А. Чтобы точно знать, что я имею в виду, вы можете изучить этот пост . Надеюсь, у вас есть идея.
источник