प्रतिक्रिया कार्यात्मक घटक सदस्यता के समय राज्य का स्नैपशॉट ले रहा है।

पूर्व के लिए। पीएफबी कोड।

अगर मैं सेटसॉकेटहैंडलर बटन पर क्लिक करता हूं और फिर सेटवेलकमस्ट्रिंग बटन दबाता हूं। अब अगर मैं वेलकमस्ट्रिंग लॉग करते समय सॉकेट पर संदेश प्राप्त करता हूं तो यह खाली है।

लेकिन अगर मैं सेटवेलकमस्ट्रिंग बटन पर क्लिक करता हूं और फिर सेटसॉकेटहैंडलर बटन पर क्लिक करता हूं। अब अगर मुझे सॉकेट पर संदेश प्राप्त होता है तो आपका स्वागत कंसोल पर लॉग हो रहा है।

मैंने प्रोजेक्ट में समान व्यवहार देखा है इसलिए साबित करने के लिए बस यह सरल ऐप बनाया है।

अगर मैं नीचे टिप्पणी की गई कक्षा घटक का उपयोग करता हूं .. सब कुछ ठीक काम करता है।

तो मेरा सवाल यह है कि रिएक्ट फंक्शनल कंपोनेंट रेग के समय एक स्टेट पर काम कर रहा है न कि मैसेज मिलने के समय वास्तविक स्थिति पर।

यह बहुत अजीब है। इसे कार्यात्मक घटक में सही तरीके से कैसे काम करें।

import React, {useEffect, useState} from 'react';
import logo from './logo.svg';
import './App.css';
const io = require('socket.io-client');
const socket = io.connect('http://localhost:3000/');


const App : React.FunctionComponent = () => {

    const [welcomeString, setWelcomeString] = useState("");

    const buttonCliecked = () => {
        console.log("clocked button");
        setWelcomeString("Welcome")
    }


    const onsockethandlerclicked = () => {
        console.log("socket handler clicked");
        socket.on('out', () => {
            console.log("Recived message")
            console.log(welcomeString);
        });
    }

    return (
        <div>
            <header className="component-header">User Registration</header>
            <label>{welcomeString}</label>
            <button onClick={buttonCliecked}>setWelcomeString</button>
            <button onClick={onsockethandlerclicked}>setSocketHandler</button>
        </div>
    );
}

/*class App extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            welcomeString:""
        }
    }


    buttonCliecked = () => {
        console.log("clocked button");
        this.setState({ welcomeString:"Welcome"})
    }


    onsockethandlerclicked = () => {
        console.log("socket handler clicked");
        socket.on('out', () => {
            console.log("Recived message")
            console.log(this.state.welcomeString);
        });
    }

    render() {
        return (
            <div>
                <header className="component-header">User Registration</header>
                <label>{this.state.welcomeString}</label>
                <button onClick={this.buttonCliecked}>setwelcomestring</button>
                <button onClick={this.onsockethandlerclicked}>setSocketHandler</button>
            </div>
        );
    }
}*/


export default App;

1
user3087500 21 सितंबर 2019, 00:05

1 उत्तर

सबसे बढ़िया उत्तर

हममें से जो Redux पृष्ठभूमि से आते हैं, उनके लिए useReducer भ्रामक रूप से जटिल और अनावश्यक लग सकता है। यूजस्टेट और संदर्भ के बीच, यह सोच के जाल में पड़ना आसान है कि एक रेड्यूसर अधिकांश सरल उपयोग के मामलों के लिए अनावश्यक जटिलता जोड़ता है; हालाँकि, यह पता चला है कि useReducer राज्य प्रबंधन को बहुत सरल कर सकता है। आइए एक उदाहरण देखें।

मेरी अन्य पोस्टों की तरह, यह कोड मेरी बुकलिस्ट प्रोजेक्ट से है। उपयोग का मामला यह है कि एक स्क्रीन उपयोगकर्ताओं को पुस्तकों में स्कैन करने की अनुमति देती है। ISBN को रिकॉर्ड किया जाता है, और फिर एक सीमित दर वाली सेवा को भेजा जाता है जो पुस्तक की जानकारी देखती है। चूंकि लुकअप सेवा सीमित है, इसलिए इस बात की गारंटी देने का कोई तरीका नहीं है कि आपकी पुस्तकों को जल्द ही देखा जाएगा, इसलिए एक वेब सॉकेट स्थापित किया गया है; जैसे ही अपडेट आते हैं, संदेश ws के नीचे भेजे जाते हैं, और ui में हैंडल किए जाते हैं। Ws की एपीआई गंदगी सरल है: डेटा पैकेट में एक _messageType संपत्ति होती है, बाकी वस्तु पेलोड के रूप में काम करती है। जाहिर है एक अधिक गंभीर परियोजना कुछ मजबूत डिजाइन करेगी।

घटक वर्गों के साथ, ws सेट करने के लिए कोड सीधा था: कंपोनेंटडिडमाउंट में ws सब्सक्रिप्शन बनाया गया था, और कंपोनेंटविल में इसे फाड़ दिया गया था। इसे ध्यान में रखते हुए, हुक के साथ निम्नलिखित प्रयास करने के जाल में पड़ना आसान है

const BookEntryList = props => {
  const [pending, setPending] = useState(0);
  const [booksJustSaved, setBooksJustSaved] = useState([]);

  useEffect(() => {
    const ws = new WebSocket(webSocketAddress("/bookEntryWS"));

    ws.onmessage = ({ data }) => {
      let packet = JSON.parse(data);
      if (packet._messageType == "initial") {
        setPending(packet.pending);
      } else if (packet._messageType == "bookAdded") {
        setPending(pending - 1 || 0);
        setBooksJustSaved([packet, ...booksJustSaved]);
      } else if (packet._messageType == "pendingBookAdded") {
        setPending(+pending + 1 || 0);
      } else if (packet._messageType == "bookLookupFailed") {
        setPending(pending - 1 || 0);
        setBooksJustSaved([
          {
            _id: "" + new Date(),
            title: `Failed lookup for ${packet.isbn}`,
            success: false
          },
          ...booksJustSaved
        ]);
      }
    };
    return () => {
      try {
        ws.close();
      } catch (e) {}
    };
  }, []);

  //...
};

हम एक खाली निर्भरता सूची के साथ ws निर्माण को useEffect कॉल में डालते हैं, जिसका अर्थ है कि यह फिर से आग नहीं करेगा, और हम टियरडाउन करने के लिए एक फ़ंक्शन लौटाते हैं। जब घटक पहली बार माउंट होता है, तो हमारा ws सेट हो जाता है, और जब घटक अनमाउंट हो जाता है, तो यह टूट जाता है, ठीक वैसे ही जैसे हम एक क्लास कंपोनेंट के साथ करते हैं।

समस्या

यह कोड बुरी तरह विफल रहता है। हम useEffect क्लोजर के अंदर स्टेट एक्सेस कर रहे हैं, लेकिन उस स्टेट को डिपेंडेंसी लिस्ट में शामिल नहीं कर रहे हैं। उदाहरण के लिए, useEffect के अंदर लंबित का मान हमेशा शून्य होगा। निश्चित रूप से, हम ws.onmessage हैंडलर के अंदर सेटपेंडिंग को कॉल कर सकते हैं, जो उस राज्य को अपडेट करने और घटक को फिर से प्रस्तुत करने का कारण बनता है, लेकिन जब यह हमारे उपयोग को फिर से प्रस्तुत करता है तो फिर से आग नहीं लगेगी (फिर से, खाली निर्भरता के कारण सूची) - जिसके परिणामस्वरूप लंबित के लिए अब-बासी मूल्य पर बंद होना बंद हो जाएगा।

स्पष्ट होने के लिए, नीचे चर्चा की गई हुक लाइनिंग नियम का उपयोग करके, इसे आसानी से पकड़ लिया जाएगा। अधिक मौलिक रूप से, कक्षा घटक दिनों से पुरानी आदतों को तोड़ना आवश्यक है। इन निर्भरता सूचियों को कंपोनेंटडिडमाउंट / कंपोनेंटडिडअपडेट / कंपोनेंटविल अनमाउंट फ्रेम ऑफ माइंड से न देखें। सिर्फ इसलिए कि इसके वर्ग घटक संस्करण ने एक बार वेब सॉकेट स्थापित किया होगा, कंपोनेंटडिडमाउंट में, इसका मतलब यह नहीं है कि आप एक खाली निर्भरता सूची के साथ एक उपयोग प्रभाव कॉल में सीधा अनुवाद कर सकते हैं।

ओवरथिंक न करें, और होशियार न हों: आपके रेंडर फ़ंक्शन के दायरे से कोई भी मूल्य जो प्रभाव कॉलबैक में उपयोग किया जाता है, को आपकी निर्भरता सूची में जोड़ा जाना चाहिए: इसमें प्रॉप्स, स्टेट आदि शामिल हैं। उसने कहा-

समाधान

जबकि हम अपनी उपयोग की निर्भरता सूची में आवश्यक स्थिति के हर टुकड़े को जोड़ सकते हैं, इससे वेब सॉकेट टूट जाएगा, और हर अपडेट पर फिर से बनाया जाएगा। यह शायद ही कुशल होगा, और वास्तव में समस्याएं पैदा कर सकता है यदि डब्ल्यूएस निर्माण पर प्रारंभिक स्थिति का एक पैकेट भेजता है, जिसका पहले से ही हिसाब लगाया जा सकता है, और हमारे यूआई में अपडेट किया जा सकता है।

हालांकि, अगर हम करीब से देखें, तो हमें कुछ दिलचस्प दिखाई दे सकता है। हम जो भी ऑपरेशन कर रहे हैं वह हमेशा पूर्व स्थिति के संदर्भ में होता है। हम हमेशा कुछ कह रहे हैं जैसे "लंबित पुस्तकों की संख्या में वृद्धि," "इस पुस्तक को पूर्ण की सूची में जोड़ें," आदि। ठीक यही वह जगह है जहां एक रेड्यूसर चमकता है; वास्तव में, एक नए राज्य को प्रोजेक्ट पूर्व राज्य को आदेश भेजना एक रेड्यूसर का पूरा उद्देश्य है।

इस पूरे राज्य प्रबंधन को एक रेड्यूसर में ले जाने से स्थानीय राज्य के किसी भी संदर्भ को useEffect कॉलबैक के भीतर समाप्त कर दिया जाएगा; आइए देखें कैसे।

function scanReducer(state, [type, payload]) {
  switch (type) {
    case "initial":
      return { ...state, pending: payload.pending };
    case "pendingBookAdded":
      return { ...state, pending: state.pending + 1 };
    case "bookAdded":
      return {
        ...state,
        pending: state.pending - 1,
        booksSaved: [payload, ...state.booksSaved]
      };
    case "bookLookupFailed":
      return {
        ...state,
        pending: state.pending - 1,
        booksSaved: [
          {
            _id: "" + new Date(),
            title: `Failed lookup for ${payload.isbn}`,
            success: false
          },
          ...state.booksSaved
        ]
      };
  }
  return state;
}
const initialState = { pending: 0, booksSaved: [] };

const BookEntryList = props => {
  const [state, dispatch] = useReducer(scanReducer, initialState);

  useEffect(() => {
    const ws = new WebSocket(webSocketAddress("/bookEntryWS"));

    ws.onmessage = ({ data }) => {
      let packet = JSON.parse(data);
      dispatch([packet._messageType, packet]);
    };
    return () => {
      try {
        ws.close();
      } catch (e) {}
    };
  }, []);

  //...
};

जबकि थोड़ी और लाइनें, अब हमारे पास कई अपडेट फ़ंक्शन नहीं हैं, हमारा उपयोग प्रभाव शरीर बहुत अधिक सरल और पठनीय है, और हमें अब बंद होने की स्थिति के बारे में चिंता करने की ज़रूरत नहीं है: हमारे सभी अपडेट हमारे एकल रेड्यूसर के खिलाफ प्रेषण के माध्यम से होते हैं . यह टेस्टेबिलिटी में भी सहायता करता है, क्योंकि हमारे रेड्यूसर का परीक्षण करना अविश्वसनीय रूप से आसान है; यह सिर्फ एक वेनिला जावास्क्रिप्ट फ़ंक्शन है। जैसा कि रिएक्ट टीम के सुनील पई कहते हैं, रिड्यूसर का उपयोग करने से लिखने से अलग पढ़ने में मदद मिलती है। हमारा उपयोग प्रभाव निकाय अब केवल उन कार्यों को भेजने के बारे में चिंतित है, जो नई स्थिति उत्पन्न करते हैं; इससे पहले यह मौजूदा राज्य को पढ़ने और नए राज्य को लिखने दोनों से संबंधित था।

आपने देखा होगा कि रेड्यूसर को शून्य स्लॉट में टाइप के साथ, एक प्रकार की कुंजी के साथ ऑब्जेक्ट के रूप में, एक सरणी के रूप में भेजा जा रहा है। या तो useReducer के साथ अनुमति है; यह सिर्फ एक चाल है डैन अब्रामोव ने मुझे बॉयलरप्लेट को थोड़ा कम करने के लिए दिखाया :) कार्यात्मक सेटस्टेट के बारे में क्या ()

अंत में, आप में से कुछ लोग सोच रहे होंगे कि, मूल कोड में, मैंने ऐसा क्यों नहीं किया

सेटपेंडिंग (लंबित => लंबित - 1 || 0);

इसके बजाय

सेटपेंडिंग (लंबित - 1 || 0);

यह बंद करने की समस्या को दूर कर देता, और इस विशेष उपयोग के मामले के लिए ठीक काम करता; हालांकि, किताबों के मिनट अपडेट के लिए जस्ट सेव्ड को लंबित मूल्य तक पहुंच की आवश्यकता होती है, या इसके विपरीत, यह समाधान टूट जाता है, हमें वहीं छोड़ देता है जहां हमने शुरू किया था। इसके अलावा, मुझे लगता है कि रेड्यूसर संस्करण थोड़ा क्लीनर है, राज्य प्रबंधन अपने स्वयं के रेड्यूसर फ़ंक्शन में अच्छी तरह से अलग हो गया है।

सब कुछ, मुझे लगता है कि useReducer() वर्तमान में अविश्वसनीय रूप से कम उपयोग किया जाता है। यह कहीं भी उतना डरावना नहीं है जितना आप सोच सकते हैं। इसे आज़माइए!

हैप्पी कोडिंग!

0
user3087500 2 अक्टूबर 2019, 00:25