การทำความเข้าใจ React-Redux และ mapStateToProps ()


220

ฉันพยายามที่จะเข้าใจวิธีการเชื่อมต่อของ react-redux และฟังก์ชั่นที่ใช้เป็นพารามิเตอร์ mapStateToProps()โดยเฉพาะอย่างยิ่ง

วิธีที่ฉันเข้าใจมันมูลค่าส่งคืนของmapStateToPropsจะเป็นวัตถุที่ได้มาจากสถานะ (ตามที่อาศัยอยู่ในร้านค้า) ซึ่งจะมีการส่งคีย์ไปยังองค์ประกอบเป้าหมายของคุณ

ซึ่งหมายความว่าสถานะที่ใช้งานโดยองค์ประกอบเป้าหมายของคุณสามารถมีโครงสร้างที่แตกต่างจากรัฐในขณะที่เก็บไว้ในร้านค้าของคุณ

ถาม: ตกลงไหม
ถาม: นี่เป็นสิ่งที่คาดหวังหรือไม่
ถาม: นี่เป็นรูปแบบต่อต้านหรือไม่


11
ฉันไม่ต้องการที่จะเพิ่มคำตอบให้กับการผสมผสาน ... แต่ฉันรู้ว่าไม่มีใครตอบคำถามของคุณได้จริง ... ในความคิดของฉันมันไม่ใช่รูปแบบการต่อต้าน กุญแจสำคัญอยู่ในชื่อ mapStateTo Propsคุณจะผ่านคุณสมบัติแบบอ่านอย่างเดียวสำหรับส่วนประกอบที่จะใช้ ฉันมักจะใช้ส่วนประกอบคอนเทนเนอร์ของฉันเพื่อรับสถานะและเปลี่ยนมันก่อนที่จะส่งไปยังส่วนประกอบการนำเสนอ
Matthew Brent

3
วิธีนี้องค์ประกอบงานนำเสนอของฉันง่ายกว่ามาก ... ฉันอาจแสดงผลthis.props.someDataตรงข้ามกับthis.props.someKey[someOtherKey].someData... เข้าท่าใช่ไหม?
Matthew Brent

3
บทช่วยสอนนี้อธิบายได้ดีพอ: learn.co/lessons/map-state-to-props-readme
Ayan

สวัสดี Pablo โปรดพิจารณาคำตอบที่คุณเลือกอีกครั้ง
vsync

พิจารณาใหม่ได้อย่างไร
Pablo Barría Urenda

คำตอบ:


56

ถามIs this ok?
: ใช่

ถาม: Is this expected?
ใช่คาดว่าเป็นเช่นนี้ (หากคุณใช้ react-redux)

ถาม: Is this an anti-pattern?
ตอบ: ไม่ใช่นี่ไม่ใช่รูปแบบต่อต้าน

มันเรียกว่า "การเชื่อมต่อ" ส่วนประกอบของคุณหรือ "ทำให้มันฉลาด" มันเกิดจากการออกแบบ

ช่วยให้คุณสามารถแยกส่วนประกอบของคุณจากสถานะของคุณอีกครั้งซึ่งจะเป็นการเพิ่มความเป็นโมดุลของรหัสของคุณ นอกจากนี้ยังช่วยให้คุณทำให้สถานะคอมโพเนนต์ของคุณเป็นส่วนย่อยของสถานะแอปพลิเคชันของคุณได้ง่ายขึ้นซึ่งอันที่จริงแล้วช่วยให้คุณปฏิบัติตามรูปแบบ Redux

คิดแบบนี้: ร้านค้าควรจะมี สถานะทั้งหมดของใบสมัครของคุณ
สำหรับแอปพลิเคชันขนาดใหญ่สิ่งนี้อาจมีคุณสมบัติหลายสิบซ้อนกันหลายชั้นลึก
คุณไม่ต้องการลากสิ่งที่อยู่รอบ ๆ ในการโทรแต่ละครั้ง (แพง)

โดยไม่ต้องmapStateToPropsอะนาล็อกหรือบางส่วนนั้นคุณจะถูกล่อลวงไปแกะสลักขึ้นรัฐของคุณวิธีการปรับปรุงประสิทธิภาพการทำงาน / ลดความซับซ้อนอีก


6
ฉันไม่คิดว่าการอนุญาตให้แต่ละองค์ประกอบเข้าถึงร้านค้าทั้งหมดได้ แต่สิ่งที่มีขนาดใหญ่อาจมีประสิทธิภาพในการทำงาน การส่งวัตถุไปรอบ ๆ นั้นไม่ใช้หน่วยความจำเนื่องจากเป็นวัตถุเดียวกันเสมอ เหตุผลเพียงอย่างเดียวที่นำมาสู่ชิ้นส่วนที่ต้องการคือ 2 เหตุผล: (1) -การเข้าถึงที่ง่ายยิ่งขึ้น(2) -หลีกเลี่ยงข้อผิดพลาดที่ส่วนประกอบอาจเลอะสถานะไม่ได้เป็นของมัน
vsync

@vsync คุณช่วยอธิบายวิธีการที่ทำให้การเข้าถึงแบบลึกง่ายขึ้นได้อย่างไร คุณหมายถึงว่าคุณสามารถใช้อุปกรณ์ประกอบฉากในท้องถิ่นได้แทนที่จะต้องอ้างถึงรัฐทั่วโลกและสามารถอ่านได้มากขึ้น?
Siddhartha

นอกจากนี้องค์ประกอบที่อาจทำให้เกิดความยุ่งเหยิงของรัฐไม่ได้เป็นของมันเมื่อรัฐถูกส่งผ่านเป็นไม่เปลี่ยนรูป?
Siddhartha

ถ้ารัฐไม่เปลี่ยนรูปแล้วฉันคิดว่ามันดี แต่ก็ยังเป็นแนวปฏิบัติที่ดีมันจะดีกว่าถ้าจะเปิดเผยองค์ประกอบเฉพาะส่วนที่เกี่ยวข้อง สิ่งนี้ยังช่วยให้ผู้พัฒนารายอื่นเข้าใจได้ดีขึ้นว่าส่วนใด (ของวัตถุสถานะ ) ที่เกี่ยวข้องกับองค์ประกอบนั้น เกี่ยวกับ "การเข้าถึงที่ง่ายขึ้น" มันง่ายกว่าในแง่ที่ว่าเส้นทางสู่บางรัฐลึกถูกส่งผ่านไปยังองค์ประกอบโดยตรงในฐานะเสาและส่วนประกอบนั้นมืดบอดกับความจริงที่ว่ามี 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 ..

ตัวอย่างของ actionCreator: 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

@ilyo เหล่านี้เป็นผู้สร้างแอ็คชั่นของคุณคุณต้องนำเข้า
webdeb

2
คำตอบที่ดี! ฉันคิดว่ามันเป็นการดีที่จะเน้นว่าโค้ดอันนี้state => state.posts( mapStateToPropsฟังก์ชั่น) จะบอก React ว่าสถานะใดที่จะทริกเกอร์การแสดงผลคอมโพเนนต์อีกครั้งเมื่ออัพเดต
Miguel Péres

38

คุณมีส่วนแรกที่ถูกต้อง:

ใช่mapStateToPropsมีสถานะร้านค้าเป็นอาร์กิวเมนต์ / พารามิเตอร์ (จัดทำโดยreact-redux::connect) และใช้เพื่อเชื่อมโยงส่วนประกอบกับส่วนที่แน่นอนของสถานะร้านค้า

โดยการเชื่อมโยงที่ผมหมายถึงวัตถุที่ส่งกลับโดยmapStateToPropsจะมีการจัดที่เวลาในการก่อสร้างเป็นอุปกรณ์ประกอบฉากและการเปลี่ยนแปลงใด ๆ componentWillReceivePropsที่ตามมาจะสามารถใช้ได้ผ่าน

หากคุณรู้ว่ารูปแบบการออกแบบของผู้สังเกตการณ์เป็นสิ่งที่เกิดขึ้นจริงหรือไม่ก็เปลี่ยนแปลงเล็กน้อย

ตัวอย่างจะช่วยทำให้สิ่งต่าง ๆ ชัดเจนขึ้น:

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สิ่งที่อยู่ในตัวอย่างนี้จะทริกเกอร์ refilter ของรายการและรีเฟรชจอแสดงผลเนื่องจากข้อเท็จจริงที่ว่าปฏิกิริยาตอบสนองเปลี่ยนไป .

แจ้งให้เราทราบหากตัวอย่างสับสนหรือไม่ชัดเจนเพียงพอที่จะให้คำอธิบายที่ดีกว่า

สำหรับ: ซึ่งหมายความว่าสถานะที่ใช้งานโดยองค์ประกอบเป้าหมายของคุณสามารถมีโครงสร้างที่แตกต่างจากรัฐในขณะที่เก็บไว้ในร้านของคุณ

ฉันไม่ได้รับคำถาม แต่เพิ่งรู้ว่าสถานะการตอบกลับ ( this.setState) แตกต่างจากสถานะ Redux Store โดยสิ้นเชิง!

สถานะการตอบสนองจะใช้ในการจัดการการวาดใหม่และพฤติกรรมของส่วนประกอบการตอบสนอง สถานะปฏิกิริยามีอยู่ในส่วนประกอบเท่านั้น

สถานะ Redux Store เป็นการรวมกันของ Redux reducers state แต่ละตัวมีหน้าที่จัดการตรรกะแอพขนาดเล็ก แอ็ตทริบิวต์ reducers เหล่านั้นสามารถเข้าถึงได้โดยความช่วยเหลือของreact-redux::connect@mapStateToPropsองค์ประกอบใด ๆ ! ซึ่งทำให้แอพสามารถเข้าถึงสถานะการจัดเก็บ Redux ได้กว้างในขณะที่สถานะคอมโพเนนต์นั้นไม่รวมอยู่ในตัวของมันเอง


5

นี้ตอบสนองและReduxตัวอย่างจะตามออกตัวอย่างเช่นโมฮาเหม็ Mellouki ของ แต่จะตรวจสอบการใช้เสริมสวยและlinting กฎ โปรดทราบว่าเรากำหนดอุปกรณ์ประกอบฉากและวิธีการจัดส่งโดยใช้PropTypesเพื่อให้ผู้แปลของเราไม่ได้กรีดร้องที่เรา ตัวอย่างนี้รวมรหัสบางบรรทัดที่ขาดหายไปในตัวอย่างของ Mohamed เชื่อมต่อกับการใช้งานคุณจะต้องนำเข้าจากตอบสนอง-Redux ตัวอย่างนี้ยังผูก filterItems วิธีนี้จะป้องกันไม่ขอบเขตปัญหาในองค์ประกอบ รหัสแหล่งที่มานี้ได้รับการจัดรูปแบบอัตโนมัติโดยใช้ JavaScript เสริมสวย

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 container ทำ)

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);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.