Get the content size of view React Native outputs? - react-native

React Native provides a view class called RCTRootView. You specify the frame in which the RootView should render things, and it goes ahead and does it.
In my use-case though, the size of the content view is dynamic, and depends on the data being passed into React Native. This size is important because I'm going to put the React Native view into e.g. a self-sizing cell.
How is this done?

#fatuhoku I came to the same conclusion after some digging around. For anyone else who's looking to do this, here's an example.
Declare your bridge to native code where we'll be sending the content
size (where depends on preference, I create the instance outside of
any class/function at the top of the file).
Call a method in the onLayout property of a JSX component.
In the onLayout method being called, send the content size data to the bridge.
Use the content size in the native ObjC or Swift code as you need.
// MyReactComponent.jsx (ES6)
// Modules
var React = require('react-native');
var { View } = React;
// Bridge
var ReactBridge = require('NativeModules').ReactBridge;
// Type Definitions
type LayoutEvent = {
nativeEvent: {
layout: {
x: number;
y: number;
width: number;
height: number;
};
};
};
// React Component
class MyReactComponent extends React.Component {
onViewLayout(e: LayoutEvent) {
ReactBridge.setLayout(e.nativeEvent.layout);
}
render() {
return (
<View onLayout={ this.onViewLayout }>
<ChildComponentExample />
</View>
);
}
}
Then in ObjC you use the RCT_EXPORT_METHOD as described in React Native's documentation to create the "ReactBridge" and handle the the data sent on the "setLayout" method. The data will be an NSDictionary in this case but you could just as easily send only the layout height and you'd then get a CGFloat.

Related

How to pass a color prop in React Native

I would like to pass color prop to native sdk UI. I was trying to pass it in style prop but it did not help. Does anyone know, how to pass color on the native side?
that's strange that you have a problem with passing staff through flat props and styles object as it should be handled out of the box.
Simple case
The only thing that you need to do is mark the property with color type, on IOS.
RCT_EXPORT_VIEW_PROPERTY(myMagicColorProp, UIColor)
On Android colors are just simply integers so you should just simply use Integer
#ReactProp(name = "myMagicColor")
fun setColor(view: View, color: Int?) {
// ...
}
Complex case
If you are trying to pass color in some nested structure e.g.
<MyComponent data={{ magicColor: "red"}} />
You have to handle parsing color on your own.
Thankfully facebook gave us some tools for that.
First of all, you have to normalize the color on the JS side
import { processColor } from 'react-native'
<MyComponent data={{ magicColor: processColor("red") }} />
Next on IOS you have to use RCTConvert to get UIColor
#objc func setData(_ data: NSDictionary) {
let color = RCTConvert.uiColor(data["magicColor"])
/// ...
}
On Android you don't have to do anything more than just simply use getInt
#ReactProp(name = "data")
fun setData(view: View, data: ReadableMap?) {
view.setBackgroundColor(data.getInt("color"))
/// ...
}

Query in react native about sliding up panel

In React Native iOS, I would like to slide in and out of a like in the following picture.
So I installed this https://github.com/octopitus/rn-sliding-up-panel for ease.
but this error is showing =>
i cant understand whats wrong, I am new to react native. Please Help!
You cannot access variable called _panel from this object because you are inside a function itself. besides you are using function based react, in order to create a reference check useRef() hook or switch to class based component and then you can use this._panel;
smthg like this:
function AccessingElement() {
const elementRef = useRef();
const onPress = () => {
// e.g
elementRef.current.show();
}
return (
<View ref={elementRef}>
...child views
</View>
);
}

Is there a way to animate the increased size of a View when new children are added?

I’m currently using LayoutAnimation to animate a view when children are added. However, since LayoutAnimation causes everything to be animated, globally, and I can’t easily use built-in Animated library to fit my use-case, I’m wondering if react-native-reanimated is able to help.
Here's a snack of my current solution:
https://snack.expo.io/#insats/height-adapation
This is what the result of that looks like:
Is there a way to achieve the same thing without using LayoutAnimation? I've looked through all exampled in react-native-reanimated, and I've read through the docs but I'm still not sure if this is possible to do or how I should get started. I've thought about using Animated to move the item-wrapper out of the viewable area and "scroll" it upwards (using transform translateY) when items are added, but that would require fixed height, which I don't have.
I have 2 approaches that I can suggest out of my mind:
You can configure your LayoutAnimation only when your desired state changed. If you use hooks it would be too easy:
const [state,setState] = useState([]);
useEffect(()=>{
/*rest code*/
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
},[state])
Or if you use class component you can catch your desired state change in componentDidUpdate:
componentDidUpdate(prevProps,prevState){
if(prevState.items!==state.items){
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
}
}
You can use onLayout function of view:
addItem = () => {
this.setState({
items: [...this.state.items, {title:'An item',isNew:true}]
})
};
renderItems = () => {
return this.state.items.map((item, index) => {
let opacity = new Animated.Value(0);
return (
<Animated.View onLayout={({nativeEvent})=>{
if(this.state.item.isNew){
// here you got the height from nativeEvent.layout.height
// Then you have to store the height animate height and opacity to its precise value
// PS I used opacity:0 to calculate the height
}
}} key={index} style={[styles.item,{opacity}>
<Text>{item.title}</Text>
</View>
)
});
};
When it comes to react-native-reanimated I regard it as more faster version of react-native's Animated library. So either way you will have to calculate the height!

React Native: How can I use the DeviceInfo isTablet() method to conditionally apply a style to an element?

I am trying to adapt the design of my app to tablet and one way to detect if the app is running on a tablet is by using the DeviceInfo module in particular the isTablet() method. How can I use this method to conditionally apply styles to an element?
Here is what I am trying to do at the moment:
import { checkIfDeviceIsTablet } from './helper-functions';
<View style={[styles.wrapper, checkIfDeviceIsTablet() === true ? styles.wrapperTablet : {}]}>
{contents}
</View>
The checkIfDeviceIsTablet() function is as follows:
import DeviceInfo from 'react-native-device-info';
function checkIfDeviceIsTablet() {
DeviceInfo.isTablet().then(isTablet => {
return isTablet;
});
}
The issue is that when the component loads the checkIfDeviceIsTablet() method returns a promise as opposed to the expected true/false value and so the conditional styles are not applied when the app is run on a tablet. I tried turning the function into an async/await format with a try/catch but the result is the same.
I would use React Native's own Platform.isPad function but the app must also work on Android.
Any help is appreciated.
I would recommend calling DeviceInfo.isTablet() only once at the beginning of your app. You can store the result globally, and then later on you can check the type without having to deal with async promises.
To store the type globally, your options are:
A global variable
React's Context API
A static property on a class (if using ES6+)
Some sort of global state management solution like Redux
You still have to deal with the initial async problem, since the first call to DeviceInfo.isTablet() will return an async promise.
I'd recommend looking into React's Context API.
Here's a rough example:
render() {
return (
<DeviceInfoContext.Consumer>
{ ({ isTablet }) => (
<Text>Is this a tablet? {isTablet}</Text>
) }
</DeviceInfoContext.Consumer>
)
}
And your DeviceInfoContext class would look something like this:
class DeviceInfoContext extends React.Component {
state = {
isTablet: false
}
componentDidMount() {
Device.IsTablet().then(result => this.setState({ isTablet: result }))
}
render() {
return (
this.props.children({ isTablet: this.state.isTablet })
)
}
}
This is just a rough example. You can learn more about the Context API in the docs
Me too had some troubles with the breaking changes of react native 0.5xx to 0.6xx. The library for device detection change it structure to promises. A paintful.
This library save the day, the installation and use is very easy.
https://github.com/m0ngr31/react-native-device-detection
import { isTablet } from 'react-native-device-detection;
// isTablet is a boolean. Return false o true immediately
//So ...
import styled from 'styled-components/native';
import theme from 'styled-theming';
import { isTablet } from 'react-native-device-detection';
const CoverPageDateText = styled.Text`
font-size: ${isTablet ? 23 : 17};
color: gray;
padding-bottom: 9;
`

React native detect screen rotation

I'm using onLayout to detect screen orientation and it's working fine inside my root view, but when I implemented inside the drawer it didn't work, any reason why this happens ?
code :
import Drawer from 'react-native-drawer'
...
onLayout(e) {
console.log('onLayout');
}
<Drawer onLayout={this.onLayout}
It didn't log any thing when orientation changed!
This is because the Drawer component doesn't take onLayout as a prop. You can see in the source code that the rendered View does use onLayout, but it's not pulling from something like this.props.onLayout.
I'm not exactly sure what you're looking to do, but maybe this issue will help you. As it shows, you can pass a function into openDrawerOffset instead of an integer or a ratio in order to be a little more dynamic with how you set your offset:
openDrawerOffset={(viewport) => {
if (viewport.width < 400) {
return viewport.width * 0.1;
}
return viewport.width - 400;
}}
You might also benefit from the Event handlers that react-native-drawer has to offer.