reactjs شرح - هل يمكنك إجبار مكون React على إعادة تجميع دون استدعاء setState؟




ماهو ترجمة (9)

لقد وجدت أنه من الأفضل تجنب forceUpdate (). إحدى الطرق لفرض إعادة العرض هي إضافة تبعية التقديم () على متغير خارجي مؤقت وتغيير قيمة ذلك المتغير عند الحاجة.

فيما يلي مثال الكود:

class Example extends Component{
   constructor(props){
      this.state = {temp:0};

      this.forceChange = this.forceChange.bind(this);
   }

   forceChange(){
      this.setState(prevState => ({
          temp: prevState.temp++
      })); 
   }

   render(){
      return(
         <div>{this.state.temp &&
             <div>
                  ... add code here ...
             </div>}
         </div>
      )
   }
}

استدعاء this.forceChange () عندما تحتاج إلى فرض إعادة التقديم.

لدي عنصر خارجي (للمكون) ، كائن مرئي أريد الاستماع إليه لإجراء تغييرات عليه. عند تحديث الكائن ، فإنه يُصدر أحداث التغيير ، ثم أريد إعادة توجيه المكون عند اكتشاف أي تغيير.

باستخدام React.render ذي المستوى الأعلى ، React.render هذا ممكنًا ، ولكن في أحد المكونات لا يعمل (وهو أمر منطقي نظرًا لأن طريقة render فقط بإرجاع كائن).

فيما يلي مثال الكود:

export default class MyComponent extends React.Component {

  handleButtonClick() {
    this.render();
  }

  render() {
    return (
      <div>
        {Math.random()}
        <button onClick={this.handleButtonClick.bind(this)}>
          Click me
        </button>
      </div>
    )
  }
}

يؤدي النقر فوق الزر داخليًا إلى استدعاء this.render() ، ولكن هذا ليس السبب this.render() في حدوث التقديم (يمكنك رؤية ذلك في الإجراء لأن النص الذي تم إنشاؤه بواسطة {Math.random()} لا يتغير. ومع ذلك ، إذا كنت ببساطة استدعاء this.setState() بدلاً من this.render() ، يعمل بشكل جيد.

لذا أعتقد أن سؤالي هو: هل تحتاج مكونات React إلى حالة من أجل إعادة العرض؟ هل هناك طريقة لفرض العنصر على التحديث عند الطلب دون تغيير الحالة؟


يجب تجنب forceUpdate لأنه ينحرف عن عقلية React. تستشهد مستندات forceUpdate مثالاً على متى يمكن استخدام forceUpdate :

بشكل افتراضي ، عندما تتغير حالة المكون أو الدعائم ، سيتم إعادة عرض المكون الخاص بك. ومع ذلك ، إذا كان هذا التغيير ضمنيًا (على سبيل المثال: بيانات عميقة داخل كائن تتغير بدون تغيير الكائن نفسه) أو إذا كانت طريقة العرض () تعتمد على بعض البيانات الأخرى ، فيمكنك إخبار React أنها تحتاج إلى إعادة تشغيل التقديم () عن طريق الاتصال تحديث بالقوة().

ومع ذلك ، أود أن أقترح فكرة أنه حتى مع الأجسام المتداخلة بشدة ، فإن forceUpdate غير ضروري. باستخدام تغييرات تعقب مصدر البيانات غير القابلة للتغيير تصبح رخيصة. سيؤدي التغيير دائمًا إلى كائن جديد ، لذا نحتاج فقط إلى التحقق مما إذا تم تغيير المرجع إلى الكائن. يمكنك استخدام المكتبة Immutable JS لتنفيذ كائنات البيانات غير القابلة للتغيير في تطبيقك.

عادة يجب أن تحاول تجنب جميع استخدامات forceUpdate () وقراءة فقط من هذا .props و this.state في التقديم (). هذا يجعل المكون الخاص بك "خالص" والتطبيق الخاص بك أبسط بكثير وأكثر كفاءة. https://facebook.github.io/react/docs/component-api.html#forceupdate

سيؤدي تغيير مفتاح العنصر الذي تريد إعادة عرضه إلى العمل. اضبط مفتاح الدعامة على العنصر الخاص بك عن طريق الحالة ، ثم عندما تريد تحديث حالة الإعداد ليكون لها مفتاح جديد.

<Element key={this.state.key} /> 

ثم يحدث تغيير وإعادة تعيين المفتاح

this.setState({ key: Math.random() });

أريد أن أشير إلى أن هذا سيحل محل العنصر الذي يتغير المفتاح. مثال على المكان الذي يمكن أن يكون فيه هذا مفيدًا هو عندما يكون لديك حقل إدخال للملفات وترغب في إعادة تعيينه بعد تحميل الصورة.

في حين أن الجواب الحقيقي على سؤال OP سيكون forceUpdate() لقد وجدت هذا الحل مفيد في حالات مختلفة. أود أيضًا أن أشير إلى أنك إذا وجدت نفسك تستخدم forceUpdate فقد ترغب في مراجعة شفرتك ومعرفة ما إذا كانت هناك طريقة أخرى للقيام بالأشياء.


لذا أعتقد أن سؤالي هو: هل تحتاج مكونات React إلى حالة من أجل إعادة العرض؟ هل هناك طريقة لفرض العنصر على التحديث عند الطلب دون تغيير الحالة؟

لقد حاولت الإجابات الأخرى توضيح كيف يمكنك ذلك ، لكن النقطة هي أنه لا يجب عليك . حتى الحل الهاكر لتغيير المفتاح يخطئ الهدف. إن قوة React تتخلى عن التحكم في الإدارة اليدوية عندما يجب تقديم شيء ما ، وبدلاً من ذلك فقط تتعلق بكيفية وضع شيء ما على المدخلات. ثم توفير تيار المدخلات.

إذا كنت بحاجة إلى إعادة التقديم يدويًا ، فأنت بالتأكيد لا تفعل شيئًا صحيحًا.


لا يوجد قلق من الدولة لإعادة تقديمها

كنت أقوم بتخزين البيانات الخاصة بي بشكل كبير عندما بدأت في استخدام أسلوب التفكير المفترض ، وكان علي أن أفعل ذلك للتقديم. كان هذا خطأ جدا. اتضح ، يمكنك حتى تجنب القيام بأشياء معقدة مثل Flux و Redux أيضًا إذا قمت بإدراج بعض المتاجر البسيطة الثابتة.

فئة BaseView لترث من التحديثات مخزن ومعالجتها

import React, { Component } from 'react';
import { View } from 'react-native';

export default class BaseView extends Component {

  constructor(props) {
    super(props)
    this.state = {}
  }

  onChange = () => {
    this.setState(this.state) // dumb easy: triggers render
  }

  componentWillMount = () => {
    this.stores && this.stores.forEach(store => {
      // each store has a common change event to subscribe to
      store.on('change', this.onChange)
    })
  }

  componentWillUnmount = () => {
    this.stores && this.stores.forEach(store => {
      store.off('change', this.onChange)
    })
  }

}

كيف تستعمل

import React, { Component } from 'react'
import { View, Text } from 'react-native'
import BaseView from './BaseView'
import myStore from './myStore'

class MyView extends BaseView {
  contructor(props) {
    super(props)
    this.stores = [myStore]
  }

  render() {
    return (<View><Text onPress={() => {
      myStore.update({ message: 'hi' + Date.now() })
    }}>{myStore.get().message}</Text></View>)
  }
}

سبيل المثال الطبقة متجر بسيط

var ee = require('event-emitter')
export default class Store {
  constructor() {
    this._data = {}
    this._eventEmitter = ee({})
  }
  get() {
    return {...this._data} // immutable
  }
  update(newData) {
    this._data = {..._this.data, ...newData}
    this._eventEmitter.emit('change')
  }
  on(ev, fn) {
    this._eventEmitter.on(ev, fn)
  }
  off(ev, fn) {
    this._eventEmitter.off(ev, fn)
  }
}

متجر سبيل المثال المفرد

import Store from './Store'

const myStore = new Store()
export default myStore

أعتقد أن هذا يلغي جميع الأطر القاسية اللازمة للتطبيقات الأصغر. استخدم redux للوسط إلى الكبيرة على الرغم من أنه مقترن بـ Immutable.js.


يمكنك فعل ذلك بطريقتين:

1. استخدم طريقة forceUpdate() :

هناك بعض الأخطاء التي قد تحدث عند استخدام طريقة forceUpdate() . أحد الأمثلة على ذلك أنه يتجاهل أسلوب shouldComponentUpdate() وسيتم إعادة عرض العرض بغض النظر عن ما إذا كان shouldComponentUpdate() إرجاع shouldComponentUpdate() false. وبسبب هذا باستخدام forceUpdate () ينبغي تجنبه متى كان ذلك ممكنا.

2. تمرير هذا. الدولة إلى طريقة setState()

يتغلب سطر التعليمات البرمجية التالي على المشكلة مع المثال السابق:

this.setState(this.state);

حقا كل هذا يفعله هو الكتابة على الوضع الحالي مع الحالة الحالية التي تؤدي إلى إعادة تقديم. هذا ليس بالضرورة أفضل طريقة لفعل الأشياء ، لكنه يتغلب على بعض الأخطاء التي قد تواجهها باستخدام طريقة forceUpdate ().


I forceUpdate by doing following

WRONG WAY : لا تستخدم الفهرس كمفتاح

 this.state.rows.map((item, index) =>
                        <MyComponent cell={item} key={index} />
                    )

الطريقة الصحيحة : استخدام معرف البيانات كمفتاح. يمكن أن يكون بعض guid وغيرها

 this.state.rows.map((item, index) =>
                        <MyComponent item={item} key={item.id} />
                    )

لذلك من خلال القيام بتحسين الكود ، سيكون مكونك فريدًا ويجعله طبيعيًا


عندما تريد أن يتواصل مكونان React ، وهما غير مرتبطين بعلاقة (parent-child) ، فمن المستحسن استخدام Flux أو هندسة معمارية مشابهة.

ما تريد القيام به هو الاستماع إلى التغييرات التي تمت في مخزن المكونات الذي يمكن ملاحظته ، والذي يحتوي على النموذج والواجهة الخاصة به ، وحفظ البيانات التي تتسبب في تغيير العرض state في MyComponent . عندما يدفع المخزن البيانات الجديدة ، تقوم بتغيير حالة المكون الخاص بك ، والذي يقوم بتشغيل العرض تلقائيًا.

عادة يجب محاولة تجنب استخدام forceUpdate() . من الوثائق:

عادة يجب أن تحاول تجنب جميع استخدامات forceUpdate () وقراءة فقط من هذا .props و this.state في التقديم (). هذا يجعل تطبيقك أبسط بكثير وأكثر كفاءة


في الواقع ، forceUpdate() هو الحل الصحيح الوحيد كما setState() قد لا يؤدي إلى إعادة تقديم إذا تم تنفيذ منطق إضافي في shouldComponentUpdate() أو عندما يقوم ببساطة بإرجاع false .

تحديث بالقوة()

استدعاء forceUpdate() سيتسبب render() ليتم استدعاؤها على المكون ، يجب تخطي shouldComponentUpdate() . https://facebook.github.io/react/docs/component-api.html#forceupdate

setState ()

دوماً سوف setState() تشغيل re-render ما لم يتم تطبيق منطق تقديم الشرطي في shouldComponentUpdate() . more...

يمكن استدعاء forceUpdate() من خلال المكون الخاص بك من خلال هذا. this.forceUpdate()


مع 1.0 ، يعتمد React-Router على وحدة history باعتبارها peerDependency. هذه الوحدة تتعامل مع التوجيه في المتصفح. بشكل افتراضي ، يستخدم pushState -Router واجهة برمجة تطبيقات سجل HTML5 ( pushState ، replaceState ) ، ولكن يمكنك تهيئتها لاستخدام التوجيه المستند إلى التجزئة (انظر أدناه)

يتم الآن معالجة الطريق خلف الكواليس ، ويرسل ReactRouter الدعائم الجديدة لأسفل إلى معالجات المسار عندما يتغير المسار. يحتوي جهاز التوجيه على onUpdate رد onUpdate جديدة عند إجراء onUpdate عند تغيير المسار ، أو مفيد لتتبع مشاهدة الصفحة ، أو تحديث <title> ، على سبيل المثال.

العميل (توجيه HTML5)

import {Router} from 'react-router'
import routes from './routes'

var el = document.getElementById('root')

function track(){
  // ...
}

// routes can be children
render(<Router onUpdate={track}>{routes}</Router>, el)

العميل (التوجيه المستند إلى التجزئة)

import {Router} from 'react-router'
import {createHashHistory} from 'history'
import routes from './routes'

var el = document.getElementById('root')

var history = createHashHistory()

// or routes can be a prop
render(<Router routes={routes} history={history}></Router>, el)

الخادم

على الخادم ، يمكننا استخدام ReactRouter.match ، وهذا مأخوذ من دليل تقديم الخادم

import { renderToString } from 'react-dom/server'
import { match, RoutingContext } from 'react-router'
import routes from './routes'

app.get('*', function(req, res) {
  // Note that req.url here should be the full URL path from
  // the original request, including the query string.
  match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
    if (error) {
      res.status(500).send(error.message)
    } else if (redirectLocation) {
      res.redirect(302, redirectLocation.pathname + redirectLocation.search)
    } else if (renderProps) {
      res.status(200).send(renderToString(<RoutingContext {...renderProps} />))
    } else {
      res.status(404).send('Not found')
    }
  })
})




reactjs react-jsx