setState ไม่อัปเดตสถานะทันที


113

ฉันอยากจะถามว่าทำไมสถานะของฉันถึงไม่เปลี่ยนไปเมื่อฉันทำกิจกรรม onclick ฉันค้นหามานานแล้วว่าฉันต้องการผูกฟังก์ชัน onclick ในตัวสร้าง แต่สถานะยังไม่อัปเดต นี่คือรหัสของฉัน:

import React from 'react';

import Grid from 'react-bootstrap/lib/Grid';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';

import BoardAddModal from 'components/board/BoardAddModal.jsx';

import style from 'styles/boarditem.css';

class BoardAdd extends React.Component {

    constructor(props){
        super(props);

        this.state = {
            boardAddModalShow: false
        }

        this.openAddBoardModal = this.openAddBoardModal.bind(this);
    }
    openAddBoardModal(){
        this.setState({ boardAddModalShow: true });
// After setting a new state it still return a false value
        console.log(this.state.boardAddModalShow);

    }

    render() {

        return (
            <Col lg={3}>
                <a href="javascript:;" className={style.boardItemAdd} onClick={this.openAddBoardModal}>
                    <div className={[style.boardItemContainer,style.boardItemGray].join(' ')}>
                        Create New Board
                    </div>
                </a>



            </Col>
        )
    }
}

export default BoardAdd

5
คำตอบที่คุณยอมรับสำหรับคำถามนี้ไม่สมเหตุสมผล setStateไม่คืนคำสัญญา หากใช้งานได้มันจะใช้งานได้เพียงเพราะawaitแนะนำ "ติ๊ก" แบบ async หนึ่งรายการในฟังก์ชันและเกิดขึ้นว่าการอัปเดตสถานะได้รับการประมวลผลระหว่างการทำเครื่องหมายนั้น ไม่รับประกัน ตามคำตอบนี้คุณต้องใช้การโทรกลับที่สมบูรณ์ (หากคุณต้องการทำอะไรบางอย่างจริงๆหลังจากที่สถานะได้รับการอัปเดตซึ่งเป็นเรื่องผิดปกติโดยปกติคุณแค่ต้องการแสดงผลอีกครั้งซึ่งจะเกิดขึ้นโดยอัตโนมัติ)
TJ Crowder

จะเป็นการดีหากคุณยกเลิกการยอมรับคำตอบที่ยอมรับในปัจจุบันหรือยอมรับคำตอบที่ถูกต้องเพราะอาจถูกใช้ซ้ำสำหรับคำถามอื่น ๆ อีกมากมาย การมีคำตอบที่ไม่ถูกต้องที่ด้านบนจะทำให้เข้าใจผิด
Guy Incognito

คำตอบ:


11

การโทรกลับนี้ยุ่งจริงๆ เพียงใช้ async รอแทน:

async openAddBoardModal(){
    await this.setState({ boardAddModalShow: true });
    console.log(this.state.boardAddModalShow);
}

33
นั่นไม่สมเหตุสมผล React setStateไม่คืนคำสัญญา
TJ Crowder

18
@TJCrowder พูดถูก setStateไม่คืนคำสัญญาดังนั้นจึงไม่ควรรอ ที่กล่าวว่าฉันคิดว่าฉันสามารถเข้าใจได้ว่าทำไมสิ่งนี้ถึงใช้ได้กับบางคนเพราะการรอคอยทำให้การทำงานภายในของ setState บน call stack นำหน้าฟังก์ชันที่เหลือดังนั้นจึงได้รับการประมวลผลก่อนและดูเหมือนว่าสถานะจะเป็นเช่นนั้น ชุด. หาก setState มีหรือใช้การเรียกแบบอะซิงโครนัสใหม่คำตอบนี้จะล้มเหลวหากต้องการใช้ฟังก์ชันนี้อย่างถูกต้องคุณสามารถใช้สิ่งนี้:await new Promise(resolve => this.setState({ boardAddModalShow: true }, () => resolve()))
Mike Richards

นรกasync this.setState({})แก้ไขปัญหาของฉันได้อย่างไร เรามีโค้ดที่ใช้งานได้มีการพัฒนาเพิ่มเติม แต่ไม่มีอะไรเปลี่ยนแปลงส่วนนั้นของแอป มีเพียงหนึ่งในสองวัตถุใน setState เท่านั้นที่ได้รับการอัปเดตทำให้ async ส่งคืนฟังก์ชันการทำงานกลับมาและขณะนี้วัตถุทั้งสองกำลังอัปเดตอย่างถูกต้อง ฉันไม่รู้ว่ามันผิดอะไร
OndřejŠevčík

ฉันพบคำถามนี้ แต่วิธีนี้ใช้ไม่ได้ผลสำหรับฉันฉันยังไม่ได้แก้ปัญหาของฉัน แต่ฉันพบว่าอ่านดีๆที่นี่: ozmoroz.com/2018/11/why-my-setstate-doesnt-work
carmolim

156

สถานะของคุณต้องใช้เวลาในการกลายพันธุ์และเนื่องจากconsole.log(this.state.boardAddModalShow)ดำเนินการก่อนที่สถานะจะกลายพันธุ์คุณจะได้รับค่าก่อนหน้านี้เป็นเอาต์พุต ดังนั้นคุณต้องเขียนคอนโซลในการเรียกกลับไปที่setStateฟังก์ชัน

openAddBoardModal() {
  this.setState({ boardAddModalShow: true }, function () {
    console.log(this.state.boardAddModalShow);
  });
}

setStateเป็นแบบอะซิงโครนัส หมายความว่าคุณไม่สามารถเรียกมันในบรรทัดเดียวและถือว่าสถานะมีการเปลี่ยนแปลงในวันถัดไป

อ้างอิงจากReact docs

setState()ไม่กลายพันธุ์ทันทีthis.stateแต่สร้างการเปลี่ยนสถานะที่รอดำเนินการ การเข้าถึงthis.stateหลังจากเรียกเมธอดนี้อาจส่งคืนค่าที่มีอยู่ได้ ไม่มีการรับประกันการทำงานแบบซิงโครนัสของการโทรไปยัง setState และการโทรอาจเป็นกลุ่มเพื่อเพิ่มประสิทธิภาพ

ทำไมพวกเขาถึงสร้างsetStateasync

เนื่องจากมีการsetStateเปลี่ยนแปลงสถานะและทำให้เกิดการแสดงผล นี่อาจเป็นการดำเนินการที่มีราคาแพงและการทำซิงโครนัสอาจทำให้เบราว์เซอร์ไม่ตอบสนอง

ดังนั้นการsetStateโทรจึงเป็นแบบอะซิงโครนัสและเป็นกลุ่มเพื่อประสบการณ์และประสิทธิภาพ UI ที่ดีขึ้น


จนถึงตอนนี้มันเป็นความคิดที่ดี แต่จะเกิดอะไรขึ้นถ้าเราไม่สามารถใช้ console.log ได้เพราะคุณใช้กฎ eslint?
maudev

2
@maudev กฎของ eslint จะหยุดให้คุณมี console.logs เพื่อที่จะไม่จบลงด้วยการใช้งานจริง แต่ตัวอย่างข้างต้นใช้สำหรับการดีบักเท่านั้น และ console.log และแทนที่ด้วยการกระทำที่คำนึงถึงสถานะที่อัปเดต
Shubham Khatri

1
จะเกิดอะไรขึ้นหากการโทรกลับไม่เป็นไปตามที่คุณกล่าว ของฉันไม่!
Alex Jolig

@ShubhamKhatri สิ่งนี้ใช้งานได้ดีสำหรับฉันอย่างไรก็ตามจากนั้นฉันก็พยายามส่งผ่านค่าสถานะเป็นเสาไปยังส่วนประกอบย่อยและเมื่อฉันconsole.logเป็นเสาฉันจะได้รับค่าเดียวกันกับสถานะดั้งเดิม
SirArchibald

39

โชคดี setState()ที่โทรกลับ และนี่คือที่ที่เราได้รับการปรับปรุงสถานะ

ลองพิจารณาตัวอย่างนี้

this.setState({ name: "myname" }, () => {                              
        //callback
        console.log(this.state.name) // myname
      });

ดังนั้นเมื่อการเรียกกลับเริ่มทำงาน this.state คือสถานะที่อัปเดต
คุณสามารถรับmutated/updatedข้อมูลในการโทรกลับ


1
คุณยังสามารถใช้การโทรกลับนี้เพื่อส่งผ่านฟังก์ชันใด ๆ ก็ได้initMap()เช่น
Dende

1
ทางที่จะไป! ในที่สุดก็แก้ปัญหาของฉัน ขอบคุณมาก.
Ahmet Halilović

12

เนื่องจาก setSatate เป็นฟังก์ชันแบบอะซิงโครนัสดังนั้นคุณจึงต้องคอนโซลสถานะเป็นการเรียกกลับเช่นนี้

openAddBoardModal(){
    this.setState({ boardAddModalShow: true }, () => {
        console.log(this.state.boardAddModalShow)
    });
}

7

setState()ไม่ได้อัปเดตส่วนประกอบในทันทีเสมอไป อาจเป็นกลุ่มหรือเลื่อนการอัปเดตออกไปในภายหลัง ทำให้อ่าน this.state ทันทีหลังจากเรียกsetState()ข้อผิดพลาดที่อาจเกิดขึ้น ให้ใช้componentDidUpdateหรือsetStateเรียกกลับ ( setState(updater, callback)) แทนซึ่งรับประกันว่าจะเริ่มทำงานหลังจากการอัปเดตถูกนำไปใช้ หากคุณต้องการตั้งค่าสถานะตามสถานะก่อนหน้าโปรดอ่านเกี่ยวกับอาร์กิวเมนต์ตัวอัปเดตด้านล่าง

setState()จะนำไปสู่การ re-render เสมอเว้นแต่จะshouldComponentUpdate()ส่งกลับเท็จ หากมีการใช้อ็อบเจ็กต์ที่เปลี่ยนแปลงได้และไม่สามารถใช้ตรรกะการแสดงผลแบบมีเงื่อนไขได้การshouldComponentUpdate()เรียกใช้setState()เฉพาะเมื่อสถานะใหม่แตกต่างจากสถานะก่อนหน้าจะหลีกเลี่ยงการแสดงผลซ้ำที่ไม่จำเป็น

อาร์กิวเมนต์แรกคือฟังก์ชันตัวอัปเดตพร้อมลายเซ็น:

(state, props) => stateChange

stateคือการอ้างอิงถึงสถานะของส่วนประกอบในขณะที่ทำการเปลี่ยนแปลง ไม่ควรกลายพันธุ์โดยตรง แต่ควรแสดงการเปลี่ยนแปลงโดยการสร้างวัตถุใหม่โดยอิงตามข้อมูลที่ป้อนจากสถานะและอุปกรณ์ประกอบฉาก ตัวอย่างเช่นสมมติว่าเราต้องการเพิ่มค่าในสถานะโดย props.step:

this.setState((state, props) => {
    return {counter: state.counter + props.step};
});

2

หากคุณต้องการติดตามสถานะกำลังอัปเดตหรือไม่วิธีอื่นในการทำสิ่งเดียวกันคือ

_stateUpdated(){
  console.log(this.state. boardAddModalShow);
}

openAddBoardModal(){
  this.setState(
    {boardAddModalShow: true}, 
    this._stateUpdated.bind(this)
  );
}

ด้วยวิธีนี้คุณสามารถเรียกเมธอด "_stateUpdated" ทุกครั้งที่คุณพยายามอัปเดตสถานะสำหรับการดีบัก


2

useEffectสำหรับทุกคนที่พยายามที่จะทำเช่นนี้กับตะขอที่คุณต้องการ

function App() {
  const [x, setX] = useState(5)
  const [y, setY] = useState(15) 

  console.log("Element is rendered:", x, y)

  // setting y does not trigger the effect
  // the second argument is an array of dependencies
  useEffect(() => console.log("re-render because x changed:", x), [x])

  function handleXClick() {
    console.log("x before setting:", x)
    setX(10)
    console.log("x in *line* after setting:", x)
  }

  return <>
    <div> x is {x}. </div>
    <button onClick={handleXClick}> set x to 10</button>
    <div> y is {y}. </div>
    <button onClick={() => setY(20)}> set y to 20</button>
  </>
}

เอาท์พุต:

Element is rendered: 5 15
re-render because x changed: 5
(press x button)
x before setting: 5
x in *line* after setting: 5
Element is rendered: 10 15
re-render because x changed: 10
(press y button)
Element is rendered: 10 20

1

setState()เป็นแบบอะซิงโครนัส วิธีที่ดีที่สุดในการตรวจสอบหากรัฐมีการปรับปรุงจะอยู่ในcomponentDidUpdate()และไม่ใส่หลังconsole.log(this.state.boardAddModalShow)this.setState({ boardAddModalShow: true })

ตามReact Docs

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


1

อ้างอิงจากReact Docs

การตอบสนองไม่รับประกันว่าการเปลี่ยนแปลงสถานะจะถูกนำไปใช้ทันที สิ่งนี้ทำให้การอ่าน this.state ทันทีหลังจากเรียก setState () มีศักยภาพpitfallและอาจคืนexistingค่าได้เนื่องจากasyncธรรมชาติ ให้ใช้componentDidUpdateหรือsetStateเรียกกลับที่ดำเนินการทันทีหลังจากการดำเนินการ setState สำเร็จโดยทั่วไปเราแนะนำให้ใช้componentDidUpdate()สำหรับตรรกะดังกล่าวแทน

ตัวอย่าง:

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      counter: 1
    };
  }
  componentDidUpdate() {
    console.log("componentDidUpdate fired");
    console.log("STATE", this.state);
  }

  updateState = () => {
    this.setState(
      (state, props) => {
        return { counter: state.counter + 1 };
      });
  };
  render() {
    return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
        <button onClick={this.updateState}>Update State</button>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);


0

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

ตรวจสอบนี้สำหรับข้อมูลเพิ่มเติม

ดังนั้นในกรณีของคุณคุณได้ส่งคำขอให้อัปเดตสถานะ ต้องใช้เวลาสักครู่เพื่อให้ React ตอบสนอง ดังนั้นหากคุณพยายามควบคุมสถานะทันทีคุณจะได้รับค่าเก่า


-1

เมื่อฉันรันโค้ดและตรวจสอบเอาต์พุตที่คอนโซลแสดงว่าไม่ได้กำหนด หลังจากที่ฉันค้นหารอบ ๆ และพบสิ่งที่เหมาะกับฉัน

componentDidUpdate(){}

ฉันเพิ่มวิธีนี้ในโค้ดของฉันหลังจากตัวสร้าง () ตรวจสอบวงจรชีวิตของเวิร์กโฟลว์ดั้งเดิมที่ตอบสนอง

https://images.app.goo.gl/BVRAi4ea2P4LchqJ8


-2
 this.setState({
    isMonthFee: !this.state.isMonthFee,
  }, () => {
    console.log(this.state.isMonthFee);
  })

2
นี่คือสิ่งที่คำตอบที่ได้รับการโหวตมากที่สุดอธิบายได้ แต่ไม่มีคำอธิบายใด ๆ และล่าช้าไป 3 ปี โปรดอย่าโพสต์คำตอบที่ซ้ำกันเว้นแต่คุณจะมีสิ่งที่เกี่ยวข้องเพิ่มเข้าไป
Emile Bergeron

-2

ใช่เนื่องจาก setState เป็นฟังก์ชันแบบอะซิงโครนัส วิธีที่ดีที่สุดในการตั้งค่าสถานะทันทีหลังจากที่คุณเขียน set state คือการใช้ Object.assign ดังนี้: เช่นคุณต้องการตั้งค่าคุณสมบัติ isValid เป็น true ให้ทำเช่นนี้


Object.assign (this.state, {isValid: true})


คุณสามารถเข้าถึงสถานะที่อัปเดตได้หลังจากเขียนบรรทัดนี้


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