Розуміння React-Redux та mapStateToProps ()


220

Я намагаюся зрозуміти метод підключення react-redux та функції, які він приймає як параметри. Зокрема mapStateToProps().

Як я це розумію, повернене значення mapStateToPropsбуде об’єктом, отриманим із стану (як він живе в магазині), ключі якого будуть передані вашому цільовому компоненту (компонент Connect застосовується) як реквізит.

Це означає, що стан, спожитий вашим цільовим компонентом, може мати диво відмінну структуру від стану, оскільки він зберігається у вашому магазині.

З: Це добре?
З: Чи очікується це?
Питання: Це анти-модель?


11
Я не хочу додати ще одну відповідь до суміші ... але я усвідомлюю, що ніхто насправді не відповідає на ваше запитання ... на мою думку, це НЕ анти-шаблон. Ключ знаходиться у імені mapStateTo Реквізити, ви передаєте властивості лише для читання для використання компонента. Я часто використовую свої компоненти контейнерів, щоб прийняти стан і змінити його, перш ніж передати його до компонента презентації.
Метью Брент

3
Таким чином мій презентаційний компонент набагато простіший ... Я можу бути рендерінг this.props.someDataна відміну від this.props.someKey[someOtherKey].someData... має сенс?
Меттью Брент

3
Цей підручник пояснює це досить добре: learn.co/lessons/map-state-to-props-readme
Ayan

Привіт Пабло, будь ласка, перегляньте обрану відповідь.
vsync

Перегляньте як?
Пабло Баррія Уренда

Відповіді:


56

Q: Is this ok?
А: так

Q: Is this expected?
Так, це очікується (якщо ви використовуєте реакцію-редукцію).

Q: Is this an anti-pattern?
Відповідь: Ні, це не є анти-зразком.

Це називається "підключення" вашого компонента або "зробити його розумним". Це за дизайном.

Це дозволяє відокремити ваш компонент від свого стану додатковий час, що збільшує модульність вашого коду. Це також дозволяє спростити стан компонента як підмножину стану вашої програми, що, власне, допомагає вам відповідати шаблону Redux.

Подумайте про це так: магазин повинен містити весь стан вашої заявки.
Для великих застосувань це може містити десятки властивостей, вкладених у багато шарів глибиною.
Ви не хочете перетягувати все, що навколо, на кожному дзвінку (дорого).

Без цього mapStateToPropsабо якогось його аналога, ви б спокусилися вирішити свій стан ще одним способом підвищення продуктивності / спрощення.


6
Я не думаю, що надання кожному компоненту доступу до всього магазину, якою б великою вона не була, не має нічого спільного з продуктивністю. проходження об'єктів навколо не займає пам'ять, оскільки це завжди один і той же об'єкт. Єдиною причиною залучення до компонентів потрібних йому частин є, мабуть, дві причини: (1) -Легший глибокий доступ (2) -Уникайте помилок, коли компонент може зіпсувати стан, що не належить йому
vsync

@vsync Не могли б ви пояснити, як це дозволяє полегшити глибокий доступ? Ви маєте на увазі, що тепер місцеві реквізити можна використовувати замість того, щоб посилатися на глобальну державу, і це є більш читабельним?
Сіддхартха

Крім того, як компонент може зіпсувати стан, що не належить йому, коли стан передається як незмінний?
Сіддхартха

якщо стан непорушний, то, мабуть, це добре, але все-таки, як хороша практика, краще піддавати компонентам лише ті релевантні їм частини. Це також допомагає іншим розробникам краще зрозуміти, які частини ( об'єкта стану ) відповідають цьому компоненту. Що стосується "більш легкого доступу", то в тому сенсі простіше, що шлях до якогось глибокого стану безпосередньо передається компоненту як опора, і цей компонент сліпий до того, що там Redux знаходиться за кадром. Компоненти не повинні дбати про те, яка система управління державою використовується, і вони повинні працювати лише з реквізитом, який вони отримують.
vsync

119

Так, це правильно. Це просто помічна функція, щоб мати більш простий спосіб отримати доступ до своїх властивостей штату

Уявіть, у вас є postsключ у вашому додаткуstate.posts

state.posts //
/*    
{
  currentPostId: "",
  isFetching: false,
  allPosts: {}
}
*/

І складова Posts

За замовчуванням connect()(Posts)зробить всі реквізити стану для підключеного Компонента

const Posts = ({posts}) => (
  <div>
    {/* access posts.isFetching, access posts.allPosts */}
  </div> 
)

Тепер, коли ви карта state.postsна ваш компонент, це стає трохи приємніше

const Posts = ({isFetching, allPosts}) => (
  <div>
    {/* access isFetching, allPosts directly */}
  </div> 
)

connect(
  state => state.posts
)(Posts)

mapDispatchToProps

зазвичай треба писати dispatch(anActionCreator())

з bindActionCreatorsтобою можна це зробити і легше, як

connect(
  state => state.posts,
  dispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)
)(Posts)

Тепер ви можете використовувати його у своєму компоненті

const Posts = ({isFetching, allPosts, fetchPosts, deletePost }) => (
  <div>
    <button onClick={() => fetchPosts()} />Fetch posts</button>
    {/* access isFetching, allPosts directly */}
  </div> 
)

Оновлення на actionCreators ..

Приклад діїCreator: deletePost

const deletePostAction = (id) => ({
  action: 'DELETE_POST',
  payload: { id },
})

Отже, bindActionCreatorsпросто прийме ваші дії, загорне їх на dispatchдзвінок. (Я не читав вихідний код redux, але реалізація може виглядати приблизно так:

const bindActionCreators = (actions, dispatch) => {
  return Object.keys(actions).reduce(actionsMap, actionNameInProps => {
    actionsMap[actionNameInProps] = (...args) => dispatch(actions[actionNameInProps].call(null, ...args))
    return actionsMap;
  }, {})
}

Я думаю, що я можу щось пропустити, але звідки dispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)беруться дії fetchPostsта deletePostдії?
ільо

@ilyo - це твої творчі дії, вам доведеться імпортувати їх
webdeb

2
Гарна відповідь! Я думаю, що також приємно підкреслити, що цей фрагмент коду state => state.posts( mapStateToPropsфункція) підкаже React, які стани спричинить повторне відображення компонента при оновленні.
Мігель Перес

38

Ви отримали першу частину правильно:

Так mapStateToProps, стан Архів є аргументом / парам (надається користувачемreact-redux::connect ) і використовується для зв'язку компонента з певною частиною стану магазину.

Під посиланням я маю на увазі, що повернутий об'єкт mapStateToPropsбуде наданий під час будівництва як реквізит, і будь-яка наступна зміна буде доступна через componentWillReceiveProps.

Якщо ви знаєте шаблон дизайну Observer, це саме такий або невеликий його варіант.

Приклад допоможе зробити речі зрозумілішими:

import React, {
    Component,
} from 'react-native';

class ItemsContainer extends Component {
    constructor(props) {
        super(props);

        this.state = {
            items: props.items, //provided by connect@mapStateToProps
            filteredItems: this.filterItems(props.items, props.filters),
        };
    }

    componentWillReceiveProps(nextProps) {
        this.setState({
            filteredItems: this.filterItems(this.state.items, nextProps.filters),
        });
    }

    filterItems = (items, filters) => { /* return filtered list */ }

    render() {
        return (
            <View>
                // display the filtered items
            </View>
        );
    }
}

module.exports = connect(
    //mapStateToProps,
    (state) => ({
        items: state.App.Items.List,
        filters: state.App.Items.Filters,
        //the State.App & state.App.Items.List/Filters are reducers used as an example.
    })
    // mapDispatchToProps,  that's another subject
)(ItemsContainer);

Може бути інший компонент реагування, який називається itemsFiltersдисплеєм та збереженням стану фільтра в стан Redux Store, демонстраційний компонент "прослуховує" або "підписався" на фільтри стану Redux Store, тому всякий раз, коли фільтри зберігають стан змін (за допомогою filtersComponent) реагують -redux виявить, що відбулася зміна, і сповістить або "опублікує" всі прослуховувані / підписані компоненти, надіславши зміни до їхcomponentWillReceiveProps які в цьому прикладі запустять перезарядку елементів та оновлять показ через те, що стан реакції змінився .

Повідомте мене, чи є приклад заплутаним чи недостатньо зрозумілим, щоб дати краще пояснення.

Що стосується: Це означає, що стан, спожитий вашим цільовим компонентом, може мати диво відмінну структуру від стану, оскільки він зберігається у вашому магазині.

Я не отримав питання, але просто знаю, що реакційний стан ( this.setState) абсолютно відрізняється від стану магазину Redux Store!

Стан реагування використовується для обробки перемальовування та поведінки компонента, що реагує. Стан реагування міститься виключно до компонента.

Стан магазину Redux - це комбінація станів редукторів Redux, кожне з яких відповідає за управління логікою невеликої порції додатка. До цих атрибутів редукторів можна отримати доступ за допомогою react-redux::connect@mapStateToPropsбудь-якого компонента! Завдяки чому стан додатка магазину Redux є доступним для програми, тоді як стан компонента є ексклюзивним для себе.


5

Це реагувати і перевождь приклад базується на прикладі Мухаммеда Mellouki в. Але перевіряє, використовуючи правила приміркування та зв’язування . Зауважте, що ми визначаємо наші реквізити та методи відправки за допомогою PropTypes, щоб наш компілятор не кричав на нас. Цей приклад також включав деякі рядки коду, які були відсутні у прикладі Мохамеда. Для використання connect вам потрібно буде імпортувати його з react-redux . Цей приклад також пов'язує метод filterItems, це запобігає проблемам сфери застосування компонента . Цей вихідний код був автоматично відформатований за допомогою JavaScript Prettify .

import React, { Component } from 'react-native';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

class ItemsContainer extends Component {
  constructor(props) {
    super(props);
    const { items, filters } = props;
    this.state = {
      items,
      filteredItems: filterItems(items, filters),
    };
    this.filterItems = this.filterItems.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    const { itmes } = this.state;
    const { filters } = nextProps;
    this.setState({ filteredItems: filterItems(items, filters) });
  }

  filterItems = (items, filters) => {
    /* return filtered list */
  };

  render() {
    return <View>/*display the filtered items */</View>;
  }
}

/*
define dispatch methods in propTypes so that they are validated.
*/
ItemsContainer.propTypes = {
  items: PropTypes.array.isRequired,
  filters: PropTypes.array.isRequired,
  onMyAction: PropTypes.func.isRequired,
};

/*
map state to props
*/
const mapStateToProps = state => ({
  items: state.App.Items.List,
  filters: state.App.Items.Filters,
});

/*
connect dispatch to props so that you can call the methods from the active props scope.
The defined method `onMyAction` can be called in the scope of the componets props.
*/
const mapDispatchToProps = dispatch => ({
  onMyAction: value => {
    dispatch(() => console.log(`${value}`));
  },
});

/* clean way of setting up the connect. */
export default connect(mapStateToProps, mapDispatchToProps)(ItemsContainer);

Цей приклад код - хороший шаблон для початкового місця для вашого компонента.


2

React-Redux connect використовується для оновлення магазину для всіх дій.

import { connect } from 'react-redux';

const AppContainer = connect(  
  mapStateToProps,
  mapDispatchToProps
)(App);

export default AppContainer;

Це дуже просто і чітко пояснено в цьому блозі .

Ви можете клонувати проект github або скопіювати вставити код з цього блогу, щоб зрозуміти з'єднання Redux.


хороший посібник formapStateToProps thegreatcodeadventure.com/…
zloctb

1

Ось контур / схема для опису поведінки mapStateToProps:

(Це значно спрощена реалізація того, що робить контейнер Redux.)

class MyComponentContainer extends Component {
  mapStateToProps(state) {
    // this function is specific to this particular container
    return state.foo.bar;
  }

  render() {
    // This is how you get the current state from Redux,
    // and would be identical, no mater what mapStateToProps does
    const { state } = this.context.store.getState();

    const props = this.mapStateToProps(state);

    return <MyComponent {...this.props} {...props} />;
  }
}

і далі

function buildReduxContainer(ChildComponentClass, mapStateToProps) {
  return class Container extends Component {
    render() {
      const { state } = this.context.store.getState();

      const props = mapStateToProps(state);

      return <ChildComponentClass {...this.props} {...props} />;
    }
  }
}

-2
import React from 'react';
import {connect} from 'react-redux';
import Userlist from './Userlist';

class Userdetails extends React.Component{

render(){
    return(
        <div>
            <p>Name : <span>{this.props.user.name}</span></p>
            <p>ID : <span>{this.props.user.id}</span></p>
            <p>Working : <span>{this.props.user.Working}</span></p>
            <p>Age : <span>{this.props.user.age}</span></p>
        </div>
    );
 }

}

 function mapStateToProps(state){  
  return {
    user:state.activeUser  
}

}

  export default connect(mapStateToProps, null)(Userdetails);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.