Hiểu React-Redux và mapStateToProps ()


219

Tôi đang cố gắng hiểu phương thức kết nối của phản ứng-redux và các chức năng mà nó lấy làm tham số. Đặc biệt mapStateToProps().

Theo cách hiểu của tôi, giá trị trả về mapStateToPropssẽ là một đối tượng xuất phát từ trạng thái (vì nó sống trong cửa hàng), có khóa sẽ được chuyển đến thành phần đích của bạn (kết nối thành phần được áp dụng) làm đạo cụ.

Điều này có nghĩa là trạng thái được tiêu thụ bởi thành phần mục tiêu của bạn có thể có cấu trúc cực kỳ khác với trạng thái khi nó được lưu trữ trên cửa hàng của bạn.

Q: Điều này có ổn không?
Q: Điều này có được mong đợi không?
Q: Đây có phải là một mô hình chống?


11
Tôi không muốn thêm câu trả lời khác vào hỗn hợp ... nhưng tôi nhận ra không ai thực sự trả lời câu hỏi của bạn ... theo ý kiến ​​của tôi, đó KHÔNG phải là một mô hình chống đối. Khóa nằm trong tên mapStateTo props bạn đang chuyển các thuộc tính chỉ đọc cho một thành phần để tiêu thụ. Tôi sẽ thường sử dụng các thành phần chứa của mình để lấy trạng thái và thay đổi nó trước khi chuyển nó sang thành phần trình bày.
Matthew Brent

3
Bằng cách này, thành phần trình bày của tôi đơn giản hơn nhiều ... Tôi có thể được hiển thị this.props.someDatatrái ngược với this.props.someKey[someOtherKey].someData... có ý nghĩa?
Matthew Brent

3
Hướng dẫn này giải thích nó đủ tốt: learn.co/lessons/map-state-to-props-readme
Ayan

Xin chào Pablo, xin vui lòng xem xét lại câu trả lời của bạn.
vsync

Xem xét lại như thế nào?
Pablo Barría Urenda

Câu trả lời:


56

Q: Is this ok?
A: có

Q: Is this expected?
Có, điều này được mong đợi (nếu bạn đang sử dụng phản ứng-redux).

Q: Is this an anti-pattern?
A: Không, đây không phải là một mô hình chống.

Nó được gọi là "kết nối" thành phần của bạn hoặc "làm cho nó thông minh". Đó là do thiết kế.

Nó cho phép bạn tách rời thành phần của bạn khỏi trạng thái của bạn thêm một thời gian làm tăng tính mô đun của mã của bạn. Nó cũng cho phép bạn đơn giản hóa trạng thái thành phần của mình dưới dạng tập hợp con của trạng thái ứng dụng, trên thực tế, giúp bạn tuân thủ mẫu Redux.

Hãy suy nghĩ về nó theo cách này: một cửa hàng được cho là chứa toàn bộ trạng thái của ứng dụng của bạn.
Đối với các ứng dụng lớn, điều này có thể chứa hàng tá thuộc tính được lồng sâu nhiều lớp.
Bạn không muốn chuyên chở tất cả những gì xung quanh mỗi cuộc gọi (đắt tiền).

Nếu không có mapStateToPropshoặc một số tương tự, bạn sẽ cố gắng khắc lên trạng thái của mình một cách khác để cải thiện hiệu suất / đơn giản hóa.


6
Tôi không nghĩ rằng việc cấp cho mỗi và mọi thành phần quyền truy cập vào toàn bộ cửa hàng, dù có thể lớn đến đâu, cũng có liên quan đến hiệu suất. truyền các đối tượng xung quanh không chiếm bộ nhớ vì nó luôn là cùng một đối tượng. Lý do duy nhất mang đến cho các thành phần các bộ phận mà nó cần có lẽ là 2 lý do: (1) -Truy cập sâu dễ dàng hơn (2) -Tránh các lỗi trong đó một thành phần có thể gây rối trạng thái không thuộc về nó
vsync

@vsync Bạn có thể giải thích làm thế nào cho phép truy cập sâu dễ dàng hơn không? Bạn có nghĩa là các đạo cụ địa phương bây giờ có thể được sử dụng thay vì phải tham khảo trạng thái toàn cầu và vì vậy nó dễ đọc hơn?
Siddhartha

Ngoài ra, làm thế nào một thành phần có thể gây rối trạng thái không thuộc về nó khi trạng thái được truyền vào là bất biến?
Siddhartha

Nếu trạng thái là bất biến thì tôi đoán điều đó tốt, nhưng vẫn vậy, vì thực tế tốt, tốt hơn hết là chỉ tiếp xúc với các thành phần có liên quan đến chúng. Điều này cũng giúp các nhà phát triển khác hiểu rõ hơn về phần nào (của đối tượng trạng thái ) có liên quan đến thành phần đó. Về "truy cập dễ dàng hơn", dễ hiểu hơn là đường dẫn đến một số trạng thái sâu được truyền trực tiếp đến thành phần dưới dạng một chỗ dựa và thành phần đó bị mù với thực tế là Redux đằng sau hậu trường. Các thành phần không nên quan tâm hệ thống quản lý nhà nước nào được sử dụng và chúng chỉ nên hoạt động với các đạo cụ mà chúng nhận được.
vsync

119

Vâng, đúng rồi. Đây chỉ là một chức năng của trình trợ giúp để có cách đơn giản hơn để truy cập các thuộc tính trạng thái của bạn

Hãy tưởng tượng bạn có một postschìa khóa trong Ứng dụng của bạnstate.posts

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

Và thành phần Posts

Theo mặc định connect()(Posts)sẽ làm cho tất cả các đạo cụ trạng thái có sẵn cho Thành phần được kết nối

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

Bây giờ khi bạn ánh xạ state.poststhành phần của bạn, nó sẽ đẹp hơn một chút

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

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

mapDispatchToProps

bình thường bạn phải viết dispatch(anActionCreator())

với bindActionCreatorsbạn có thể làm điều đó cũng dễ dàng hơn như

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

Bây giờ bạn có thể sử dụng nó trong Thành phần của bạn

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

Cập nhật trên ActionCreators ..

Một ví dụ về ActionCreator: deletePost

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

Vì vậy, bindActionCreatorssẽ chỉ thực hiện hành động của bạn, bọc chúng vào dispatchcuộc gọi. (Tôi đã không đọc mã nguồn của redux, nhưng việc triển khai có thể trông giống như thế này:

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

Tôi nghĩ rằng tôi có thể bỏ lỡ một cái gì đó, nhưng dispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)những gì fetchPostsdeletePosthành động được truyền từ đâu?
ilyo

@ilyo đây là những người tạo hành động của bạn, bạn phải nhập chúng
webdeb

2
Câu trả lời tốt đẹp! Tôi nghĩ thật tuyệt khi nhấn mạnh rằng đoạn mã này state => state.posts( mapStateToPropshàm) sẽ cho React biết trạng thái nào sẽ kích hoạt kết xuất lại thành phần khi được cập nhật.
Miguel Péres

38

Bạn đã có phần đầu tiên đúng:

mapStateToPropscó trạng thái Store dưới dạng đối số / param (được cung cấp bởi react-redux::connect) và được sử dụng để liên kết thành phần với một phần nhất định của trạng thái cửa hàng.

Bằng cách liên kết, tôi có nghĩa là đối tượng được trả về mapStateToPropssẽ được cung cấp tại thời điểm xây dựng dưới dạng đạo cụ và mọi thay đổi tiếp theo sẽ có sẵn thông qua componentWillReceiveProps.

Nếu bạn biết mẫu thiết kế Observer thì chính xác đó là biến thể nhỏ của nó.

Một ví dụ sẽ giúp mọi thứ rõ ràng hơn:

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);

Có thể có một thành phần phản ứng khác được gọi là itemsFiltersxử lý hiển thị và duy trì trạng thái bộ lọc thành trạng thái Redux Store, thành phần Demo là "lắng nghe" hoặc "đăng ký" các bộ lọc trạng thái Redux Store để bất cứ khi nào bộ lọc thay đổi trạng thái (với sự trợ giúp của filtersComponent) -redux phát hiện ra rằng đã có thay đổi và thông báo hoặc "xuất bản" tất cả các thành phần nghe / đăng ký bằng cách gửi các thay đổi tới chúng componentWillReceivePropstrong ví dụ này sẽ kích hoạt bộ lọc của các mục và làm mới màn hình do thực tế là trạng thái phản ứng đã thay đổi .

Hãy cho tôi biết nếu ví dụ này khó hiểu hoặc không đủ rõ ràng để cung cấp một lời giải thích tốt hơn.

Về phần: Điều này có nghĩa là trạng thái được tiêu thụ bởi thành phần mục tiêu của bạn có thể có cấu trúc cực kỳ khác với trạng thái khi nó được lưu trữ trên cửa hàng của bạn.

Tôi không nhận được câu hỏi, nhưng chỉ biết rằng trạng thái phản ứng ( this.setState) hoàn toàn khác với trạng thái Cửa hàng Redux!

Trạng thái phản ứng được sử dụng để xử lý vẽ lại và hành vi của thành phần phản ứng. Trạng thái phản ứng được chứa riêng cho thành phần.

Trạng thái Redux Store là sự kết hợp của các trạng thái giảm Redux, mỗi trạng thái chịu trách nhiệm quản lý logic ứng dụng một phần nhỏ. Những thuộc tính giảm có thể được truy cập với sự trợ giúp của react-redux::connect@mapStateToPropsbất kỳ thành phần nào! Điều này làm cho ứng dụng cửa hàng Redux có thể truy cập rộng rãi trong khi trạng thái thành phần là độc quyền cho chính nó.


5

Đây phản ứng & Redux dụ được dựa trên ví dụ Mohamed Mellouki của. Nhưng xác nhận bằng cách sử dụng các quy tắc prettifylinting . Lưu ý rằng chúng tôi xác định các đạo cụ và phương thức điều phối bằng PropTypes để trình biên dịch của chúng tôi không hét vào mặt chúng tôi. Ví dụ này cũng bao gồm một số dòng mã đã bị thiếu trong ví dụ của Mohamed. Để sử dụng kết nối, bạn sẽ cần nhập nó từ Reac-redux . Ví dụ này cũng liên kết bộ lọc phương thức. Điều này sẽ ngăn các vấn đề phạm vi trong thành phần . Mã nguồn này đã được định dạng tự động bằng cách sử dụng 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);

Mã ví dụ này là một mẫu tốt cho một nơi bắt đầu cho thành phần của bạn.


2

React-Redux connect được sử dụng để cập nhật cửa hàng cho mọi hành động.

import { connect } from 'react-redux';

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

export default AppContainer;

Nó được giải thích rất đơn giản và rõ ràng trong blog này .

Bạn có thể sao chép dự án github hoặc sao chép dán mã từ blog đó để hiểu kết nối Redux.


tốt của nhãn hiệu formapStateToProps thegreatcodeadventure.com/...
zloctb

1

Dưới đây là một phác thảo / mẫu soạn để mô tả hành vi của mapStateToProps:

(Đây là một triển khai đơn giản hóa rất nhiều những gì một container Redux làm.)

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} />;
  }
}

và tiếp theo

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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.