คอมโพเนนต์ที่เชื่อมต่อ redux รู้ได้อย่างไรว่าจะต้องแสดงผลอีกครั้งเมื่อใด


94

ฉันอาจจะพลาดอะไรบางอย่างที่ชัดเจนและอยากจะเคลียร์ตัวเอง

นี่คือความเข้าใจของฉัน
ในความไร้เดียงสาตอบสนององค์ประกอบที่เรามีและstates propsการอัปเดตstateด้วยsetStateการแสดงผลองค์ประกอบทั้งหมดอีกครั้ง propsส่วนใหญ่จะอ่านอย่างเดียวและการอัปเดตไม่สมเหตุสมผล

ในคอมโพเนนต์การตอบสนองที่สมัครสมาชิกร้านค้าสำรองผ่านบางสิ่งบางอย่างเช่นstore.subscribe(render)เห็นได้ชัดว่าแสดงผลซ้ำทุกครั้งที่มีการอัปเดตร้านค้า

react-reduxมีตัวช่วยconnect()ที่ฉีดส่วนหนึ่งของ state tree (ซึ่งเป็นที่สนใจของส่วนประกอบ) และ actionCreators propsสำหรับส่วนประกอบโดยปกติจะผ่านสิ่งต่างๆเช่น

const TodoListComponent = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

แต่ด้วยความเข้าใจว่า a setStateเป็นสิ่งจำเป็นสำหรับการTodoListComponentตอบสนองต่อการเปลี่ยนแปลงโครงสร้างของสถานะ redux (re-render) ฉันไม่พบโค้ดใด ๆstateหรือsetStateที่เกี่ยวข้องในTodoListไฟล์คอมโพเนนต์ มันอ่านประมาณนี้:

const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

ใครช่วยชี้ทางที่ถูกต้องว่าฉันขาดอะไรไป

PS ฉันตามตัวอย่างรายการสิ่งที่ต้องทำพร้อมกับแพคเกจ Redux

คำตอบ:


116

connectฟังก์ชั่นสร้างองค์ประกอบเสื้อคลุมที่เป็นสมาชิกในการจัดเก็บ เมื่อมีการส่งการดำเนินการระบบจะแจ้งการเรียกกลับของส่วนประกอบ Wrapper จากนั้นจะเรียกmapStateใช้ฟังก์ชันของคุณและเปรียบเทียบวัตถุผลลัพธ์จากเวลานี้กับวัตถุผลลัพธ์จากครั้งที่แล้วอย่างตื้น ๆ (ดังนั้นหากคุณต้องการเขียนฟิลด์ที่เก็บซ้ำซ้อนด้วยค่าเดียวกันจะไม่ทำให้เกิดการแสดงผลซ้ำ) หากผลลัพธ์แตกต่างกันผลลัพธ์จะส่งผ่านผลลัพธ์ไปยังส่วนประกอบ "จริง" ของคุณเป็นอุปกรณ์ประกอบฉาก

Dan Abramov เขียนconnectที่ ( connect.js ) เวอร์ชันที่เรียบง่ายขึ้นซึ่งแสดงให้เห็นถึงแนวคิดพื้นฐานแม้ว่าจะไม่ได้แสดงการเพิ่มประสิทธิภาพใด ๆ ก็ตาม ฉันยังมีลิงก์ไปยังบทความมากมายเกี่ยวกับประสิทธิภาพของ Reduxที่พูดถึงแนวคิดที่เกี่ยวข้อง

อัพเดต

React-Redux v6.0.0ได้ทำการเปลี่ยนแปลงภายในที่สำคัญบางประการเกี่ยวกับวิธีที่ส่วนประกอบที่เชื่อมต่อรับข้อมูลจากร้านค้า

ในส่วนนี้ฉันได้เขียนโพสต์ที่อธิบายวิธีการconnectทำงานของ API และการทำงานภายในและการเปลี่ยนแปลงเมื่อเวลาผ่านไป:

Idiomatic Redux: ประวัติและการดำเนินการของ React-Redux


ขอบคุณ! คุณเป็นคนเดียวกับที่ช่วยฉันเมื่อวันก่อน @ HN - news.ycombinator.com/item?id=12307621พร้อมลิงก์ที่เป็นประโยชน์หรือไม่? ยินดีที่ได้รู้จัก :)
Joe Lewis

4
Yup ที่ฉัน :) ฉันใช้จ่ายทางเวลามากเกินไปมองหาการอภิปรายเกี่ยวกับ Redux ออนไลน์ - Reddit, HN, SO, กลาง, และสถานที่อื่น ๆ อีกมากมาย ดีใจที่ได้ช่วยเหลือ! (นอกจากนี้ FYI ฉันขอแนะนำช่องแชท Reactiflux ใน Discord ผู้คนจำนวนมากอยู่ที่นั่นและตอบคำถามฉันมักจะออนไลน์ที่นั่นตอนเย็น US EST ลิงก์เชิญอยู่ที่reactiflux.com )
markerikson

สมมติว่าสิ่งนี้ตอบคำถามใจยอมรับคำตอบหรือไม่? :)
markerikson

เป็นการเปรียบเทียบวัตถุเองหรือคุณสมบัติของวัตถุก่อน / หลังหรือไม่? ถ้าเป็นอย่างหลังจะตรวจเฉพาะระดับแรกหรือเปล่าว่า "ตื้น" หมายความว่าอย่างไร
Andy

ใช่ "ตื้นเท่าเทียมกันตรวจสอบ" prev.a === current.a && prev.b === current.b && .....หมายถึงการเปรียบเทียบเขตข้อมูลระดับแรกของแต่ละวัตถุ: ซึ่งถือว่าการเปลี่ยนแปลงข้อมูลใด ๆ จะทำให้เกิดการอ้างอิงใหม่ซึ่งหมายความว่าจำเป็นต้องมีการอัปเดตข้อมูลที่ไม่เปลี่ยนรูปแทนการกลายพันธุ์โดยตรง
markerikson

14

คำตอบของฉันอยู่นอกช่องทางซ้ายเล็กน้อย มันให้ความกระจ่างเกี่ยวกับปัญหาที่ทำให้ฉันมาที่โพสต์นี้ ในกรณีของฉันดูเหมือนว่าแอปจะไม่แสดงผลแม้ว่าจะได้รับอุปกรณ์ประกอบฉากใหม่ก็ตาม
React devs มีคำตอบสำหรับคำถามที่ถามบ่อยบางอย่างเกี่ยวกับการปรับแต่งว่าหาก (ร้านค้า) ถูกกลายพันธุ์ 99% ของเวลาที่เป็นเหตุผลที่ตอบสนองจะไม่แสดงผลอีกครั้ง ยังไม่มีอะไรเกี่ยวกับอีก 1% การกลายพันธุ์ไม่ใช่กรณีที่นี่


TLDR;

componentWillReceivePropsเป็นวิธีที่stateสามารถซิงค์กับไฟล์props.

Edge Case: เมื่อstateอัปเดตแล้วแอปจะแสดงผลอีกครั้ง!


ปรากฎว่าหากแอปของคุณใช้งานเท่านั้น stateปรากฎเพื่อแสดงองค์ประกอบเท่านั้นpropsสามารถอัปเดตได้ แต่stateไม่สามารถอัปเดตได้ดังนั้นจึงไม่มีการแสดงผลซ้ำ

ฉันมีstateที่ขึ้นอยู่กับpropsที่ได้รับจาก storeRedux ข้อมูลที่ฉันต้องการยังไม่ได้อยู่ในร้านค้าดังนั้นฉันจึงดึงข้อมูลมาจากcomponentDidMountที่เหมาะสม ฉันได้อุปกรณ์ประกอบฉากกลับมาเมื่อตัวลดการจัดเก็บของฉันอัปเดตเนื่องจากส่วนประกอบของฉันเชื่อมต่อผ่าน mapStateToProps แต่หน้าเว็บไม่แสดงผลและสถานะยังเต็มไปด้วยสตริงว่างเปล่า

ตัวอย่างนี้กล่าวว่าผู้ใช้โหลดหน้า "แก้ไขโพสต์" จาก URL ที่บันทึกไว้ คุณสามารถเข้าถึงไฟล์postIdจาก url แต่ข้อมูลยังไม่เข้าstoreคุณจึงดึงข้อมูลมา รายการบนหน้าของคุณจะถูกควบคุมชิ้นส่วนอิเล็กทรอนิกส์ - stateข้อมูลเพื่อทั้งหมดที่คุณกำลังแสดงอยู่ใน

การใช้ redux ข้อมูลถูกดึงข้อมูลการจัดเก็บได้รับการอัปเดตและส่วนประกอบคือ connectแก้ไข แต่แอปไม่ได้แสดงถึงการเปลี่ยนแปลง เมื่อมองอย่างใกล้ชิดpropsได้รับ แต่แอปไม่อัปเดต stateไม่ได้อัปเดต

ดี, propsจะปรับปรุงและเผยแพร่ แต่stateจะไม่ คุณต้องบอกstateให้อัปเดตโดยเฉพาะ

คุณไม่สามารถทำได้ใน render()และcomponentDidMountเสร็จสิ้นแล้ว

componentWillReceiveProps คือที่ที่คุณอัปเดต stateคุณสมบัติที่ขึ้นอยู่กับpropค่าที่เปลี่ยนแปลง

ตัวอย่างการใช้งาน:

componentWillReceiveProps(nextProps){
  if (this.props.post.category !== nextProps.post.category){
    this.setState({
      title: nextProps.post.title,
      body: nextProps.post.body,
      category: nextProps.post.category,
    })
  }      
}

ฉันต้องตะโกนถึงบทความนี้ที่ให้ความกระจ่างแก่ฉันเกี่ยวกับวิธีแก้ปัญหาที่โพสต์บล็อกและ repos อื่น ๆ อีกมากมายไม่สามารถพูดถึงได้ ใครก็ตามที่มีปัญหาในการหาคำตอบสำหรับปัญหาที่ไม่ชัดเจนนี้นี่คือ:

เมธอดวงจรชีวิตของส่วนประกอบ ReactJs - การดำน้ำเชิงลึก

componentWillReceivePropsคือที่ที่คุณจะอัปเดตstateเพื่อให้ข้อมูลตรงกันpropsการอัปเดต

เมื่อstateปรับปรุงแล้วเขตขึ้นอยู่กับstate สิ่งที่ต้องทำอีกครั้งทำให้!


3
จากนั้นReact 16.3เป็นต้นไปคุณควรใช้getDerivedStateFromPropsแทนcomponentWillReceiveProps. แต่ในความเป็นจริงคุณไม่ควรทำทุกอย่างตามที่ระบุไว้ที่นี่
kyw

มันใช้งานไม่ได้เมื่อใดก็ตามที่สถานะเปลี่ยนไป componentWillReceiveProps จะใช้งานได้หลายกรณีที่ฉันลองใช้ไม่ได้ผล โดยเฉพาะอย่างยิ่งอุปกรณ์ประกอบฉากที่ซ้อนกันหลายระดับหรือซับซ้อนดังนั้นฉันต้องกำหนดองค์ประกอบด้วย ID และทริกเกอร์การเปลี่ยนแปลงและอัปเดตสถานะเพื่อแสดงข้อมูลใหม่อีกครั้ง
พฤษภาคม

0

คำตอบนี้เป็นบทสรุปของบทความของ Brian Vaughn ที่มีชื่อว่าคุณอาจไม่ต้องการสถานะที่ได้รับ (07 มิถุนายน 2018)

สถานะที่ได้มาจากอุปกรณ์ประกอบฉากเป็นรูปแบบการต่อต้านในทุกรูปแบบ รวมทั้งใช้รุ่นเก่าcomponentWillReceivePropsและรุ่นใหม่getDerivedStateFromProps.

แทนที่จะได้รับสถานะจากอุปกรณ์ประกอบฉากให้พิจารณาแนวทางแก้ไขต่อไปนี้

คำแนะนำแนวทางปฏิบัติที่ดีที่สุด 2 ข้อ

คำแนะนำ 1. ส่วนประกอบที่มีการควบคุมอย่างเต็มที่
function EmailInput(props) {
  return <input onChange={props.onChange} value={props.email} />;
}
คำแนะนำ 2. ส่วนประกอบที่ไม่มีการควบคุมอย่างเต็มที่ด้วยกุญแจ
// parent class
class EmailInput extends Component {
  state = { email: this.props.defaultEmail };

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  render() {
    return <input onChange={this.handleChange} value={this.state.email} />;
  }
}

// child instance
<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

ทางเลือกสองทางหากคำแนะนำไม่เหมาะกับสถานการณ์ของคุณไม่ว่าด้วยเหตุผลใดก็ตาม

ทางเลือกที่ 1: รีเซ็ตส่วนประกอบที่ไม่มีการควบคุมด้วย ID prop
class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail,
    prevPropsUserID: this.props.userID
  };

  static getDerivedStateFromProps(props, state) {
    // Any time the current user changes,
    // Reset any parts of state that are tied to that user.
    // In this simple example, that's just the email.
    if (props.userID !== state.prevPropsUserID) {
      return {
        prevPropsUserID: props.userID,
        email: props.defaultEmail
      };
    }
    return null;
  }

  // ...
}
ทางเลือกที่ 2: รีเซ็ตองค์ประกอบที่ไม่มีการควบคุมด้วยวิธีอินสแตนซ์
class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail
  };

  resetEmailForNewUser(newEmail) {
    this.setState({ email: newEmail });
  }

  // ...
}

-2

อย่างที่ฉันรู้เพียงสิ่งเดียวที่ redux ทำในการเปลี่ยนสถานะของร้านค้าคือการเรียกใช้ componentWillRecieveProps หากส่วนประกอบของคุณขึ้นอยู่กับสถานะที่กลายพันธุ์แล้วคุณควรบังคับให้ส่วนประกอบของคุณอัปเดตเป็นเช่นนี้

1-store State change-2-call (componentWillRecieveProps (() => {3-component state change}))

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