简体   繁体   中英

Why the event handler function cannot get the updated state object value?

Here is my code:

App.js:

import './App.css';
import { useAlarmClock } from "./useAlarmClock";
export default function App() {
  const[action,data]=useAlarmClock();
  let start=()=>{
    action.start();
  }
  return (
    <div className="App">
      <button onClick={start}>Start Alarm Clock</button>
    </div>
  );
}

useAlarmClock.js

import { useReducer } from "react";
import AlarmClock from './AlarmClock';
let reducer = (state, action) => {
    let result = { ...state };
    console.log(action);
    switch (action.type) {
        case "init":
            result = { "alarmClock": action.alarmClock }
            break;
        default: break;    
    }
    return result
}

export function useAlarmClock() {
    const [itemList, updateItemList] = useReducer(reducer, {});
    let start = () => {
        let alarmClock = new AlarmClock();
        alarmClock.on("connectionTimeout", () => {
            console.log(itemList);
        })
        alarmClock.start();
        updateItemList({ "type": "init", alarmClock })
    }
    return [{
        start: start
    }, {
        itemList
    }];
}

AlarmClock.js

export default class AlarmClock {
    constructor() {
        let connectionTimeoutHandler;

        /*=====================================================================*/
        /*        To configure handler for varies event                        */
        /*=====================================================================*/
        this.on = (eventType, param) => {
            switch (eventType) {
                case "connectionTimeout":
                    connectionTimeoutHandler = param;
                    break;
                default: break;
            }
        };
        this.start = () => {
            setTimeout(() => {
                connectionTimeoutHandler();
            }, 5000);
        }
    }
}

I expect the output of the following function:

alarmClock.on("connectionTimeout", () => {
        console.log(itemList);
    })

should be:

{
  "alarmClock":{}
}

However the actual result is as the following:

{}

So, I don't know why the console.log output does not contain the alarmClock object.

At each render a new object for itemList is created due immutability, but you have only link to the first instance of itemList in your 'connectionTimeout' callback. You can access needed version of itemList with ref hook, so you need to do smtn like this:

useAlarmClock.js

 import { useReducer } from "react"; import AlarmClock from './AlarmClock'; let reducer = (state, action) => { let result = {...state }; console.log(action); switch (action.type) { case "init": result = { "alarmClock": action.alarmClock } break; default: break; } return result } export function useAlarmClock() { const [itemList, updateItemList] = useReducer(reducer, {}); const itemListRef = useRef(itemList); itemListRef.current = itemList; let start = () => { let alarmClock = new AlarmClock(); alarmClock.on("connectionTimeout", () => { console.log(itemListRef.current); }) alarmClock.start(); updateItemList({ "type": "init", alarmClock }) } return [{ start: start }, { itemList }]; }

UPD: here is working example:

 const {useReducer, useEffect, useRef} = React; function App() { const[action,data]=useAlarmClock(); let start=()=>{ action.start(); } return ( <div className="App"> <button onClick={start}>Start Alarm Clock</button> </div> ); } let reducer = (state, action) => { let result = {...state }; console.log(action); switch (action.type) { case "init": result = { "alarmClock": action.alarmClock } break; default: break; } return result } function useAlarmClock() { const [itemList, updateItemList] = useReducer(reducer, {}); const itemListRef = React.useRef(itemList); itemListRef.current = itemList; let start = () => { let alarmClock = new AlarmClock(); alarmClock.on("connectionTimeout", () => { console.log(itemListRef.current); }) alarmClock.start(); updateItemList({ "type": "init", alarmClock }) } return [{ start: start }, { itemList }]; } class AlarmClock { constructor() { let connectionTimeoutHandler; /*=====================================================================*/ /* To configure handler for varies event */ /*=====================================================================*/ this.on = (eventType, param) => { switch (eventType) { case "connectionTimeout": connectionTimeoutHandler = param; break; default: break; } }; this.start = () => { setTimeout(() => { connectionTimeoutHandler(); }, 5000); } } } ReactDOM.render(<App />, document.getElementById("root"))
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script> <div id="root"></div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM