Skip to main content

直接操纵

有时需要直接对组件进行更改,而不使用状态/属性来触发整个子树的重新渲染。例如,在浏览器中使用 React 时,有时需要直接修改 DOM 节点,移动应用中的视图也是如此。setNativeProps 是 React Native 相当于直接在 DOM 节点上设置属性。

¥It is sometimes necessary to make changes directly to a component without using state/props to trigger a re-render of the entire subtree. When using React in the browser for example, you sometimes need to directly modify a DOM node, and the same is true for views in mobile apps. setNativeProps is the React Native equivalent to setting properties directly on a DOM node.

提醒

当频繁重新渲染造成性能瓶颈时,请使用 setNativeProps

¥Use setNativeProps when frequent re-rendering creates a performance bottleneck!

直接操作不会是你经常使用的工具。你通常只会使用它来创建连续动画,以避免渲染组件层次结构和协调许多视图的开销。setNativeProps 是命令式的,它将状态存储在原生层(DOM、UIView 等)中,而不是存储在 React 组件中,这使得你的代码更难以推断。

¥Direct manipulation will not be a tool that you reach for frequently. You will typically only be using it for creating continuous animations to avoid the overhead of rendering the component hierarchy and reconciling many views. setNativeProps is imperative and stores state in the native layer (DOM, UIView, etc.) and not within your React components, which makes your code more difficult to reason about.

在使用之前,请尝试用 setStateshouldComponentUpdate 解决你的问题。

¥Before you use it, try to solve your problem with setState and shouldComponentUpdate.

setNativeProps 与 TouchableOpacity

¥setNativeProps with TouchableOpacity

TouchableOpacity 在内部使用 setNativeProps 来更新其子组件的不透明度:

¥TouchableOpacity uses setNativeProps internally to update the opacity of its child component:

const viewRef = useRef<View>();
const setOpacityTo = useCallback(value => {
// Redacted: animation related code
viewRef.current.setNativeProps({
opacity: value,
});
}, []);

这允许我们编写以下代码并知道子级将根据点击更新其不透明度,而子级对此事实没有任何了解或需要对其实现进行任何更改:

¥This allows us to write the following code and know that the child will have its opacity updated in response to taps, without the child having any knowledge of that fact or requiring any changes to its implementation:

<TouchableOpacity onPress={handlePress}>
<View>
<Text>Press me!</Text>
</View>
</TouchableOpacity>

让我们假设 setNativeProps 不可用。我们可以用该约束实现它的一种方法是将不透明度值存储在状态中,然后每当 onPress 被触发时更新该值:

¥Let's imagine that setNativeProps was not available. One way that we might implement it with that constraint is to store the opacity value in the state, then update that value whenever onPress is fired:

const [buttonOpacity, setButtonOpacity] = useState(1);
return (
<TouchableOpacity
onPressIn={() => setButtonOpacity(0.5)}
onPressOut={() => setButtonOpacity(1)}>
<View style={{opacity: buttonOpacity}}>
<Text>Press me!</Text>
</View>
</TouchableOpacity>
);

与原始示例相比,这是计算密集型的 - 每次不透明度发生变化时,React 都需要重新渲染组件层次结构,即使视图及其子视图的其他属性没有更改。通常这种开销不是问题,但在执行连续动画和响应手势时,明智地优化组件可以提高动画的保真度。

¥This is computationally intensive compared to the original example - React needs to re-render the component hierarchy each time the opacity changes, even though other properties of the view and its children haven't changed. Usually this overhead isn't a concern but when performing continuous animations and responding to gestures, judiciously optimizing your components can improve your animations' fidelity.

如果你查看 NativeMethodsMixinsetNativeProps 的实现,你会发现它是 RCTUIManager.updateView 的封装 - 这与重新渲染产生的函数调用完全相同 - 见 ReactNativeBaseComponent 中的 receiveComponent

¥If you look at the implementation of setNativeProps in NativeMethodsMixin you will notice that it is a wrapper around RCTUIManager.updateView - this is the exact same function call that results from re-rendering - see receiveComponent in ReactNativeBaseComponent.

复合组件和 setNativeProps

¥Composite components and setNativeProps

复合组件不受原生视图支持,因此你无法在它们上调用 setNativeProps。考虑这个例子:

¥Composite components are not backed by a native view, so you cannot call setNativeProps on them. Consider this example:

如果运行此命令,你将立即看到此错误:Touchable child must either be native or forward setNativeProps to a native component。发生这种情况是因为 MyButton 不直接由应设置不透明度的原生视图支持。你可以这样想:如果你使用 createReactClass 定义一个组件,你将不会期望能够在其上设置 style 属性并使其工作 - 你需要将 style 属性传递给子组件,除非你正在封装原生组件。同样,我们将把 setNativeProps 转发到原生支持的子组件。

¥If you run this you will immediately see this error: Touchable child must either be native or forward setNativeProps to a native component. This occurs because MyButton isn't directly backed by a native view whose opacity should be set. You can think about it like this: if you define a component with createReactClass you would not expect to be able to set a style prop on it and have that work - you would need to pass the style prop down to a child, unless you are wrapping a native component. Similarly, we are going to forward setNativeProps to a native-backed child component.

将 setNativeProps 转发给子进程

¥Forward setNativeProps to a child

由于 setNativeProps 方法存在于对 View 组件的任何引用上,因此将自定义组件上的引用转发到它渲染的 <View /> 组件之一就足够了。这意味着在自定义组件上调用 setNativeProps 将具有与在封装的 View 组件本身上调用 setNativeProps 相同的效果。

¥Since the setNativeProps method exists on any ref to a View component, it is enough to forward a ref on your custom component to one of the <View /> components that it renders. This means that a call to setNativeProps on the custom component will have the same effect as if you called setNativeProps on the wrapped View component itself.

你现在可以在 TouchableOpacity 中使用 MyButton

¥You can now use MyButton inside of TouchableOpacity!

你可能已经注意到,我们使用 {...props} 将所有属性传递给子视图。原因是 TouchableOpacity 实际上是一个复合组件,因此除了依赖于其子组件之外,它还要求子组件执行触摸处理。为此,它传递回调至 TouchableOpacity 组件的 各种属性。相比之下,TouchableHighlight 由原生视图支持,只需要我们实现 setNativeProps

¥You may have noticed that we passed all of the props down to the child view using {...props}. The reason for this is that TouchableOpacity is actually a composite component, and so in addition to depending on setNativeProps on its child, it also requires that the child perform touch handling. To do this, it passes on various props that call back to the TouchableOpacity component. TouchableHighlight, in contrast, is backed by a native view and only requires that we implement setNativeProps.

setNativeProps 编辑 TextInput 值

¥setNativeProps to edit TextInput value

setNativeProps 的另一个非常常见的用例是编辑 TextInput 的值。当 bufferDelay 较低且用户键入速度非常快时,TextInput 的 controlled 属性有时会丢失字符。一些开发者更喜欢完全跳过此属性,而是在必要时使用 setNativeProps 直接操作 TextInput 值。例如,以下代码演示了点击按钮时编辑输入:

¥Another very common use case of setNativeProps is to edit the value of the TextInput. The controlled prop of TextInput can sometimes drop characters when the bufferDelay is low and the user types very quickly. Some developers prefer to skip this prop entirely and instead use setNativeProps to directly manipulate the TextInput value when necessary. For example, the following code demonstrates editing the input when you tap a button:

你可以使用 clear 方法来清除 TextInput,后者使用相同的方法清除当前输入的文本。

¥You can use the clear method to clear the TextInput which clears the current input text using the same approach.

避免与渲染函数发生冲突

¥Avoiding conflicts with the render function

如果你更新也由渲染函数管理的属性,则可能会遇到一些不可预测且令人困惑的错误,因为每当组件重新渲染且该属性发生更改时,之前从 setNativeProps 设置的任何值都将被完全忽略和覆盖。

¥If you update a property that is also managed by the render function, you might end up with some unpredictable and confusing bugs because anytime the component re-renders and that property changes, whatever value was previously set from setNativeProps will be completely ignored and overridden.

setNativeProps 和 shouldComponentUpdate

¥setNativeProps & shouldComponentUpdate

通过 智能地应用 shouldComponentUpdate,你可以避免协调未更改的组件子树所涉及的不必要的开销,以至于使用 setState 而不是 setNativeProps 可能具有足够的性能。

¥By intelligently applying shouldComponentUpdate you can avoid the unnecessary overhead involved in reconciling unchanged component subtrees, to the point where it may be performant enough to use setState instead of setNativeProps.

其他原生方法

¥Other native methods

这里描述的方法在 React Native 提供的大多数默认组件上都可用。但请注意,它们不适用于未直接由原生视图支持的复合组件。这通常包括你在自己的应用中定义的大多数组件。

¥The methods described here are available on most of the default components provided by React Native. Note, however, that they are not available on composite components that aren't directly backed by a native view. This will generally include most components that you define in your own app.

measure(callback)

确定给定视图的屏幕位置、视口中的宽度和高度,并通过异步回调返回值。如果成功,将使用以下参数调用回调:

¥Determines the location on screen, width, and height in the viewport of the given view and returns the values via an async callback. If successful, the callback will be called with the following arguments:

  • x

  • y

  • width

  • height

  • 第 X 页

    ¥pageX

  • 第 Y 页

    ¥pageY

请注意,这些测量只有在原生渲染完成后才可用。如果你需要尽快进行测量并且不需要 pageXpageY,请考虑改用 onLayout 属性。

¥Note that these measurements are not available until after the rendering has been completed in native. If you need the measurements as soon as possible and you don't need pageX and pageY, consider using the onLayout property instead.

另外,measure() 返回的宽度和高度是视口中组件的宽度和高度。如果你需要组件的实际大小,请考虑使用 onLayout 属性。

¥Also the width and height returned by measure() are the width and height of the component in the viewport. If you need the actual size of the component, consider using the onLayout property instead.

measureInWindow(callback)

确定窗口中给定视图的位置并通过异步回调返回值。如果 React 根视图嵌入到另一个原生视图中,这将为你提供绝对坐标。如果成功,将使用以下参数调用回调:

¥Determines the location of the given view in the window and returns the values via an async callback. If the React root view is embedded in another native view, this will give you the absolute coordinates. If successful, the callback will be called with the following arguments:

  • x

  • y

  • width

  • height

measureLayout(relativeToNativeComponentRef, onSuccess, onFail)

measure() 类似,但测量相对于祖级的视图,并使用 relativeToNativeComponentRef 引用指定。这意味着返回的坐标是相对于祖级视图的原点 xy 的。

¥Like measure(), but measures the view relative to an ancestor, specified with relativeToNativeComponentRef reference. This means that the returned coordinates are relative to the origin x, y of the ancestor view.

注意

也可以使用 relativeToNativeNode 处理程序(而不是引用)调用此方法,但此变体已弃用。

¥This method can also be called with a relativeToNativeNode handler (instead of reference), but this variant is deprecated.

focus()

请求给定输入或视图的焦点。触发的确切行为将取决于平台和视图类型。

¥Requests focus for the given input or view. The exact behavior triggered will depend on the platform and type of view.

blur()

从输入或视图中移除焦点。这与 focus() 相反。

¥Removes focus from an input or view. This is the opposite of focus().