یه تابستون متفاوت با یه تصمیم هوشمندانه! دوره هوش مصنوعی با تخفیف ویژه، فقط با کد AI84 دوره هوش مصنوعی با تخفیف ویژه، فقط با کد AI84
🎯 ثبت نام

آموزش دستکاری مستقیم component ها در React Native

دستکاری مستقیم component ها در React Native

گاهی اوقات لازم است که یک Component را مستقیما بدون استفاده از state/props تغییر دهیم تا کل Subtree آن دوباره render شود. برای مثال، هنگام استفاده از React در مرورگر، گاهی اوقات نیاز دارید که مستقیما یک node DOM را دستکاری کنید، و همین امر برای viewها در application های موبایل صحت دارد. setNativeProps در React Native معادل مقداردهی به property به طور مستقیم برای یک node DOM است.

استفاده از setNativeProps هنگامی که rerenderمکرر موجب کاهش Performance می شود

دستکاری مستقیم را نباید مکرر استفاده شود. به طور معمول فقط از آن برای ساخت انیمیشن های متوالی استفاده کنید تا از سربار render دوباره hierarchy Component و تطبیق view ها اجتناب کنید.

استفاده از setNativeProps ضروری است و state را در لایه native (DOM، UIView، و غیره) ذخیره می کند و نه در Component های React ، همین باعث می شود فهم کد دشوار باشد. قبل از استفاده از آن، سعی کنید مشکل خود را با setState و ShouldComponentUpdate حل کنید.

setNativeProps با TouchableOpacity

TouchableOpacity خود از setNativeProps برای بروزرسانی opacity مربوط به child component خود استفاده می کند:


1
2
3
4
5
6
setOpacityTo(value) {
  // Redacted: animation related code
  this.refs[CHILD_REF].setNativeProps({
    opacity: value
  });
},<button></button>

این به ما اجازه می دهد کدی به شیوه ی زیر بنویسیم، و مطمئن باشیم Opacity المان child در واکنش به Tap، بروزرسانی می شود. بدون اینکه المان child اطلاعی از آن داشته باشد یا نیاز به تغییری در پیاده سازی اش باشد:


1
2
3
4
5
6
< TouchableOpacity onPress={this._handlePress}>
  < View style={styles.button}>
    < Text>Press me!< /Text>
  < /View>
< /TouchableOpacity>
<button></button>

بیایید فرض کنیم استفاده از setNativeProps ممکن نبود. یک راه پیاده سازی آن ذخیره ی مقدار opacity در state، و سپس update آن مقدار در هنگام روی دادن Event onPress است:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
constructor(props) {
  super(props);
  this.state = { myButtonOpacity: 1, };
}
render() {
  return (
    < TouchableOpacity onPress={ () = > this.setState({myButtonOpacity: 0.5})}
                      onPressOut={ () => this.setState({myButtonOpacity: 1})}>
      < View style={[styles.button, {opacity: this.state.myButtonOpacity}]}>
        < Text>Press me!< /Text>
      < /View>
    < /TouchableOpacity>
  )
}
<button></button>

این روش نسبت به روش اول سربار محاسباتی زیادی دارد. React باید با هر تغییر opacity، سلسله مراتب Component را دوباره render کند، حتی با اینکه بقیه attribute های view و child های آن تغییری نکرده اند. معمولا این سربار موجب نگرانی نیست، اما هنگام اجرای پیوسته انیمیشن و پاسخ به تعاملات کاربر، بهینه سازی Component ها می تواند تاثیر مهمی در اجرای درست انیمیشن ها داشته باشد.

اگر به پیاده سازی setNativeProps در NativeMethodsMixin نگاه کنید می بینید که در واقع یک wrapper برای RCTUIManager.updateView است. این دقیقا همان فراخوانی متدی است که باعث render مجدد می شود. نگاهی به این لینک بیندازید.

Component های ترکیبی و setNativeProps

Component های ترکیبی توسط view native ها پشتیبانی نمی شوند، درنیتجه نمی توانید setNativeProps را روی آن ها فراخوانی کنید. مثال زیر را در نظر بگیرید.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
class MyButton extends React.Component {
  render() {
    return (
      < View>
        < Text>{this.props.label}< /Text>
      < /View>
    )
  }
}
export default class App extends React.Component {
  render() {
    return (
      < TouchableOpacity>
        < MyButton label="Press me!" />
      < /TouchableOpacity>
    )
  }
}
<button></button>

اگر این کد را اجرا کنید با این خطا مواجه می شوید: Touchable child must either be native or forward setNativeProps to a native component. این خطا به این دلیل اتفاق می افتد که MyButton مستقیما توسط یک view native که opacity برایش باید مقداردهی شود شود، پشتیبانی نمی شود. می توانید اینطور فکر کنید که اگر یک Component را با createReactClass تعریف کنید، نمی توانید property style روی آن set کنید و کار کند. باید property را به یکی از childهایش بدهید، مگر اینکه Component native باشد. به طور مشابه ، setNativeProps را به یک Component child که به طور native پشتیبانی می شود، انتقال دهید.

تنها کاری که باید انجام دهید این است که متد setNativeProps را روی Component داشته باشید که setNativeProps را روی child خودش با آرگومان های داده شده فراخوانی کند.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
class MyButton extends React.Component {
  setNativeProps = (nativeProps) = > {
    this._root.setNativeProps(nativeProps);
  }
  render() {
    return (
      < View ref={component => this._root = component} {...this.props}>
        < Text>{this.props.label}< /Text>
      < /View>
    )
  }
}
export default class App extends React.Component {
  render() {
    return (
      < TouchableOpacity>
        < MyButton label="Press me!" />
      < /TouchableOpacity>
    )
  }
}
<button></button>

حال می توانید از MyButton درون TouchableOpacity استفاده کنید. یک نکته برای روشن کردن مسئله: ما در اینجا به جای استفاده از string-based ref از syntax ref callback استفاده کردیم.

ممکن است متوجه شده باشید که با استفاده از {...this.props} تمام prop ها را به view child انتقال دادیم. دلیل آن است که TouchableOpacity درواقع یک Component ترکیبی است، درنتیجه علاوه بر وابسته بودن به setNativeProps روی المان child خودش، نیاز دارد که آن touch element ها را هم مدیریت کند. برای این کار، تمام propهایش را که به Component TouchableOpacity برمی گردند را نیز انتقال دهید. درمقابل، TouchableHighlight توسط یک view native پشتیبانی می شود و فقط لازم است برایش setNativeProps را پیاده سازی کنیم.

استفاده از setNativeProps برای پاک کردن مقدار TextInput

یک استفاده رایج دیگر از setNativeProps برای پاک کردن مقدار TextInput است. prop controlled مربوط به TextInput ممکن است گاهی کاراکترهای ورودی کاربر را جا بیندازد، برای مثال وقتی bufferDelay پایین است و کاربر سریع تایپ می کند. بعضی توسعه دهندگان ترجیح می دهند این prop را کلا کناربگذارند و به جای آن از setNativeProps برای مستقیم دستکاری کردن مقدار TextInput درمواقع مورد نیاز، استفاده کنند. برای مثال، در زیر کدی می بینید که پاک کردن ورودی را هنگام فشردن یک Button نشان می دهد:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React from 'react';
import { TextInput, Text, TouchableOpacity, View } from 'react-native';
export default class App extends React.Component {
  clearText = () => {
    this._textInput.setNativeProps({text: ''});
  }
  render() {
    return (
      < View style={ {flex: 1, alignItems: 'center', justifyContent: 'center'}}>
        < TextInput
          ref = { component = > this._textInput = component}
          style={ {height: 50, width: 200, marginHorizontal: 20, borderWidth: 1, borderColor: '#ccc'}}
        />
        < TouchableOpacity onPress={this.clearText}>
          < Text>Clear text< /Text>
        < /TouchableOpacity>
      < /View>
    );
  }
}
<button></button>

جلوگیری از مغایرت ها با تابع render

اگر یک attribute را تغییر دهید که توسط متد render نیز مدیریت می شود، ممکن است دچار bug های گیج کننده و غیرقابل پیش بینی شوید. چرا که هربار Component مجددا render می شود و آن attribute تغییر می کند، هرمقداری که قبلا توسط setNativeProps ست شده باشد نادیده گرفته می شود و جایگزین می شود.

setNativeProps & shouldComponentUpdate

با استفاده مناسب از shouldComponentUpdate، می توانید از سربار حاصل از تطبیق Subtree های Component هایی که تغییر نکرده اند، جلوگیری کنید، تا جایی که ممکن است performance آنقدر خوب باشد که به جای استفاده از setNativeProps از setState استفاده کنید.

متدهای native دیگر

متدهایی که در اینجا بحث شد روی اغلب Component های پیش فرض که React Native در اختیار می گذارد قابل استفاده است. با این حال، آن ها روی Component های ترکیبی که توسط view native پشتیبانی نمی شوند، قابل دسترس نیستند.

measure(callback)

مکان روی صفحه، width و height view مربوط به view داده شده را تعیین می کند و مقدار را توسط یک callback async برمی گرداند. اگر این کار موفقیت آمیز باشد، متد callback با آرگومان های زیر فراخوانی می شود:

  • x
  • y
  • width
  • height
  • pageX
  • pageY

توجه کنید این مقادیر تا زمان اتمام render شدن در پلتفرم native، در دسترس نیستند. اگر اندازه ها را سریعتر می خواهید، از onLayout prop استفاده کنید.

measureInWindow(callback)

مکان viewداده شده روی پنجره را تعیین می کند و مقادیر را توسط یک callback async برمی گرداند. اگر root view درون یک view native قرار گرفته باشد، مختصات absolute را می دهد. اگر این کار موقفیت آمیز باشد، Callback با آرگومان های زیر فراخوانی می شود:

  • x
  • y
  • width
  • height

measureLayout(relativeToNativeNode, onSuccess, onFail)

همانند measure() است، اما مقادیر را نسبت به یک element parent تعیین شده به صورت relativeToNativeNode اندازه گیری می کند. این به این معناست که x و y بازگشتی مقادیر نسبی، و نسبت به مقادیر x و y مربوط به parent view هستند.

طبق معمول، برای دستیابی به node handle native یک Component می توانید از findNodeHandle(component) استفاده کنید.


1
import {findNodeHandle} from 'react-native';<button></button>

Focus()

برای یک Input یا view درخواست focus می کند. رفتار دقیق آن به پلتفرم و نوع view بستگی دارد.

Blur()

focus را از یک Input یا view می گیرد. این کاری دقیقا عکس Focus() انجام می دهد.

1398/06/04 1882 0
نظرات شما

نظرات خود را ثبت کنید...