I am trying to implement a collapsible card in React Native, and regardless of the implementation I use, keep getting the same error:
[555,"RCTView",211,{"onLayout":true,"height":"<< NaN>>"}] is not usable as a native method argument
The problem lies in using new Animated.Value(), when I initialize it with a value, say new Animated.Value(100), the error goes away and the functionality works, but the maximum height of is that constant I put in, which is not what I want. So, it seems not passing any values to Animated.Value has it return NaN.
I'm not sure if this is the expected behavior, but I'm assuming it is not as my code is almost exactly the same as here: Make animated collapsible card component, with initial props to show or hide
I will paste my code regardless:
Card.js:
import {Text, View, TouchableHighlight, StyleSheet, Animated} from 'react-native'
export default class Card extends React.Component{
anime = {
height: new Animated.Value(),
expanded: false,
contentHeight: 0,
}
constructor(props) {
super(props);
this._initContentHeight = this._initContentHeight.bind(this);
this.toggle = this.toggle.bind(this);
this.anime.expanded = props.expanded;
}
_initContentHeight(evt) {
if (this.anime.contentHeight>0) return;
this.anime.contentHeight = evt.nativeEvent.layout.height;
this.anime.height.setValue(this.anime.expanded ? this._getMaxValue() : this._getMinValue() );
}
_getMaxValue() { return this.anime.contentHeight };
_getMinValue() { return 0 };
toggle() {
Animated.timing(this.anime.height, {
toValue: this.anime.expanded ? this._getMinValue() : this._getMaxValue(),
duration: 300,
}).start();
this.anime.expanded = !this.anime.expanded;
}
render() {
return (
<View style={styles.titleContainer}>
<View style={styles.title}>
<TouchableHighlight underlayColor="transparent" onPress={this.toggle}>
<Text>{this.props.title}</Text>
</TouchableHighlight>
</View>
<Animated.View style={[styles.content, { height: this.anime.height }]} onLayout={this._initContentHeight}>
{this.props.children}
</Animated.View>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
margin:10,
overflow:'hidden'
},
titleContainer: {
flexDirection: 'row'
},
card: {
padding: 10
}
});
SearchDisplay.js:
import { Text } from 'react-native';
import Card from '../components/Panel';
export default class SearchDisplay extends React.Component {
render (){
return (
<Card title='Customized Card 1' expanded={false}>
<Text>Hello, this is first line.</Text>
<Text>Hello, this is second line.</Text>
<Text>Hello, this is third line.</Text>
</Card>
)
}
}
Apologies in advance if it's something really obvious that I'm overlooking.
As a initial value give it a 0. Then you can try to get the height of animated.view component and set it in a value in state.
https://reactnative.dev/docs/view#onlayout
But I'm not sure you can or not place it in a Animated.Value(). Who knows, it may works :-)
Animated.Value should be initialized with some initial value like 0 or else you can directly use 0 as your initial height.
NAN means you calculating a mathematic operation but you are passing non-integer value in your functionality
Related
I have built a minimal ReactJS component to update the number of likes. All works well but the count does not update when clicked. I tried following several answers but cannot figure out why.
See the code:
import React, {useState} from 'react';
class GiveLikes extends React.Component {
// set the initial value of the likes
state = {likes:0};
// Function is called every time "likes" is clicked
likes_count = (likes) =>{
// Counter state is incremented
this.state({likes: likes+1});
}
render() {
return (
<>
<h2> {this.state.likes} </h2>
<div className="buttons">
<button style={{
fontSize: '60%',
position: 'relative',
top: '20vh',
marginRight: '5px',
backgroundColor: 'green',
borderRadius: '8%',
color: 'white',
}}
onClick={() => this.likes_count}>Likes
</button>
</div>
</>
)
}
}
export default GiveLikes;
The above code will render the following on the web browser. Clicking the "Likes" should update the value of the count, but unfortunately it does not.
Declare a constructor and initialize your state,
Use an arrow function on your likes_count() method
Use this.setState({likes: this.state.likes +1}); instead of this.state({likes: this.state.likes +1});
import React, {useState} from 'react';
class GiveLikes extends React.Component {
constructor(props) {
super(props);
this.state = {likes: 0};
}
likes_count = () => {
this.setState({likes: this.state.likes +1});
}
render() {
return (
<>
<h2> {this.state.likes} </h2>
<div className="buttons">
<button style={{
fontSize: '60%',
position: 'relative',
top: '20vh',
marginRight: '5px',
backgroundColor: 'green',
borderRadius: '8%',
color: 'white',
}}
onClick={this.likes_count}>Likes
</button>
</div>
</>
)
}
}
export default GiveLikes;
Edit:
The summary of this answer is that the state does not exist because there is no constructor for this.state
I believe the answer to be is Props are never to be updated. We are to use them as is. Sounds rigid right? But React has its reasons behind this rule and I’m pretty convinced by their reasoning. The only caveat is, though, that there are situations where we might need to initiate the update of a prop. And we will soon know how.
Consider the following line of code from a parent component:
<MyChild childName={this.state.parentName} />
Now if there is any change of name required, parentName will be changed in the parent and that change will automatically be communicated to the child as is the case with the React mechanism. This setup works in most of the scenarios.
But what if you need to update the prop of the child component, and the knowledge of the change required and the trigger to change it is only known to the child? Considering the ways of React, data can only flow from top-to-bottom i.e., from parent-to-child. So then how are we to communicate to the parent that a change to prop is required?
Answer from the following source: https://www.freecodecamp.org/news/how-to-update-a-components-prop-in-react-js-oh-yes-it-s-possible-f9d26f1c4c6d/
I've only shown the parts you need to read nothing else. Please analyse your code properly next time, this isn't a hard thing to do .
You forgot to call the function -
onClick={() => this.likes_count()}
Also, Instead of passing likes you need to use the data from state and then update it like -
likes_count = () => {
let likes = this.state.likes;
this.setState({ likes: likes + 1 });
};
Add a constructor and initialize this.state otherwise it won't be exists.
What happens is every time you re-render your component (by state or props change) you will re-create the your state again and again with {likes: 0} and it will not work.
Also, you are mixing class component and functional component syntax style, which will lead to more bugs and issues with react and your code.
Moreover, you need to put a function that will be called in onClick but you created a function that returns a function, and it is wrong in your case.
Another issue is to set your likes state using this.setState and not just calling state as a function.
import React from 'react';
class GiveLikes extends React.Component {
constructor(props) {
super(props);
this.state = {likes: 0};
}
likes_count = () => {
this.setState ({likes: this.state.likes +1});
}
render() {
return (
<>
<h2> {this.state.likes} </h2>
<div className="buttons">
<button style={{
fontSize: '60%',
position: 'relative',
top: '20vh',
marginRight: '5px',
backgroundColor: 'green',
borderRadius: '8%',
color: 'white',
}}
onClick={this.likes_count}>Likes
</button>
</div>
</>
)
}
}
export default GiveLikes;
Read about react.
Focus on your first component (look for examples with counters components online).
Don't rush into things without fully understand. React is fun.
Getting an error: TypeError: Cannot read property 'map' of undefined. Before reloading the page it's work fine. But when I'm reloading the page getting an error. I want to get object values from array.
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {fetchData} from '../../actions/fetchData';
class Menu extends Component {
componentDidMount() {
this.props.fetchData();
}
render() {
const {data, isFetching} = this.props.data;
if (isFetching) {
return (
<View>
<ActivityIndicator size={'large'} />
</View>
);
} else {
return (
<View
style={{
flex: 1,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}>
<Text>
{data.categories.map(nm => nm.name)}
{/* {console.log("data",data.categories.map(nm => nm.name))} */}
</Text>
</View>
);
}
}
}
function mapStateToProps(state) {
return {
data: state.data,
};
}
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators({fetchData}, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Menu);
I'm not a react-native developer, but have working small experience on react. Here what I can understand as given below:
this.state = {
data: this.props.data,
};
In above code in constructor, first initialize with state.data, when class instance is called and it will be undefined. Because when first class is called, this.props.data is undefined.
componentDidMount() {
this.props.fetchData();
}
After constructor's task complete, above code will be executed and it's data is shown in console.log. But returned data is never assign to any variable. That means, after fetching data, this.state.data still is undefined.
so when comes to executing <Text>{this.state.data.data.categories.map(nm => nm.name)}</Text> , this.state.data will be undefined`. To solve this problem, you can try following code:
class Menu extends Component {
componentDidMount() {
this.props.fetchData();
}
render() {
return (
<View>
<Text>{this.props.data.data.categories.map(nm => nm.name)}</Text>
</View>
);
}
}
And one last thing, I strongly recommend that, you learn react development life-cycle. Thanks
I've got this problem before and i solved it for mapping through images array , because fetching data is asynchronous it seems like data is not available at beginning of rendering and causes of err when mapping , You should handle your app and prevent rendering before data is set into the state , so implement a checker to see data is completely available in state and then let render , it's the only solution and nothing else
I have my class and I have a method and I am wondering if I could use props inside a mehtod.
Notice I try to use props in methodTwo. Is this possible? If not, is there a way I could use props in method?
import React from 'react';
import { Image, Text, View } from 'react-native';
export default class Test extends React.PureComponent {
methodOne = () => {
this.setState({
one:false,
two:false,
three:false
})
}
methodTwo = () => {
this.setState({
one:false,
two:false,
//I want to use props
three:this.props.three
})
}
render() {
return (
<View style={{ backgroundColor: 'transparent', alignItems: 'center' }}>
<Button title='one' onPress={()=>this.methodOne()}/>
// I could call i like this?
<Test three='newState'/>
</View>
);
}
}
methodTwo = () => {
this.setState({
one:false,
two:false,
three:this.props.three
})
}
props-> is the value that is been transferred from parent component to child component.
In class based component you fetch the value by using this.props.Attribute_name and in functional based component you can fetch the value using props.Attribute_name (mind functional based component dont have any concept of this)
if you want to use this.props.three ,then in parent component call (the component calling this particular component) <Test three="anyValue" /> then you can easily get this value in child component.
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class MouseWithCat extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/*
We could just swap out the <p> for a <Cat> here ... but then
we would need to create a separate <MouseWithSomethingElse>
component every time we need to use it, so <MouseWithCat>
isn't really reusable yet.
*/}
<Cat mouse={this.state} />
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<MouseWithCat />
</div>
);
}
}
The props are accessible to whole of the class scope with the syntax this.props.xxxx if you have passed it from its parent component. SO you can use in methodOne too.
You can use props inside a method. Any specific error you are facing ?.
I'm working on a React Native app and many screens has forms with text input fields.
When I press the text input, the keyboard opens. I created a floating InputAccessory component which appears at the top of the keyboard to dismiss it, with the button "Done" on it.
However now that I have this accessory, when I click an input field or press the "Next" button on the keyboard to go to the next field, the ScrollView scrolls to align the bottom of the text input with the top of the keyboard. With this floating accessory it poses problems as you can see below you can't see the content of the text input because of this accessory, and I'd like to have the scrollview scrolling a bit more to display the entire text input.
I could probably do the calculation for this and run the .scrollTo() method from the ScrollView component but this pattern is very common to my entire app and I'm looking for an elegant solution that could be generic enough every single time I import a text input and focus on it.
Do you have any suggestion?
Thanks
I got the same issue before and i have 2 different solutions , Both of them worked for me.
1- Using react-native-keyboard-aware-scroll-view , Note that this library will already contain scrollView so you remove your own scroll view and use
<KeyboardAwareScrollView>
<View>
<TextInput />
</View>
</KeyboardAwareScrollView>
You can also check documentation for more info.
This solution is easier as you don't need to handle anything by yourself, but i think you will have some issues if you want to include scrollView inside it.
2- I once created a component AvoidKeyboard that actually does something similar to your solution, but it used to translate top the whole view with the keyboard height value, this solution worked perfectly also for me.
Implementation
import React, { Component } from 'react';
import { Animated, Easing, Keyboard } from 'react-native';
import PropTypes from 'prop-types';
class AvoidKeyboard extends Component {
constructor(props) {
super(props);
this.state = {
animatedViewHeight: new Animated.Value(0),
viewHeight: 0,
};
this.setViewHeightOnce = this.setViewHeightOnce.bind(this);
this.keyboardWillShow = this.keyboardWillShow.bind(this);
this.keyboardWillHide = this.keyboardWillHide.bind(this);
this.keyboardDidShowListener = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide);
}
componentWillUnmount() {
this.keyboardDidShowListener && this.keyboardDidShowListener.remove();
this.keyboardDidHideListener && this.keyboardDidHideListener.remove();
}
setViewHeightOnce(event) {
const { height } = event.nativeEvent.layout;
if (this.state.viewHeight === 0) {
const avoidPaddingBottom = 15;
this.setState({
viewHeight: height + avoidPaddingBottom,
animatedViewHeight: new Animated.Value(height + avoidPaddingBottom),
});
}
}
keyboardWillShow(e) {
const { viewHeight } = this.state;
if (viewHeight) {
requestAnimationFrame(() => { // eslint-disable-line no-undef
Animated.timing(this.state.animatedViewHeight, {
toValue: (viewHeight - e.endCoordinates.height),
duration: 200,
delay: 0,
easing: Easing.inOut(Easing.ease),
}).start();
});
}
}
keyboardWillHide() {
requestAnimationFrame(() => { // eslint-disable-line no-undef
Animated.timing(this.state.animatedViewHeight, {
toValue: this.state.viewHeight,
duration: 200,
delay: 0,
easing: Easing.inOut(Easing.ease),
}).start();
});
}
render() {
let animatedHeight;
const { viewHeight } = this.state;
if (viewHeight > 0) {
animatedHeight = { maxHeight: this.state.animatedViewHeight };
}
return (
<Animated.View
style={[{ flex: 1, justifyContent: 'flex-end' }, animatedHeight]}
onLayout={this.setViewHeightOnce}
>
{this.props.children}
</Animated.View>
);
}
}
AvoidKeyboard.propTypes = {
children: PropTypes.node.isRequired,
};
export default AvoidKeyboard;
Now you just need to wrap your component or screen inside AvoidKeyboard and your screen height will shrink once keyboard is open, and you will be able to scroll the screen
I have had a lot of problems with keyboard in IOS. No KeyboardSpacer, react-native-keyboard-aware-scroll-view and more packages solved it.
Recently I discovered react-native-keyboard-manager and it solved all my problems without one line of code, also in modals and more (I don't have nothing to do with the author, but this package saved me the day). Give it a change.
I found a solution which doesn't involve hacky animation change.
When the keyboard opens, what I decided to do is to add some margin at the bottom of the ScrollView which correspond to the height of the InputAccessory. I then remove this margin when the keyboard closes. It looks like something like this:
import KeyboardListener from 'react-native-keyboard-listener';
...
render() [
<ScrollView
key={1}
style={{ marginBottom: this.state.scrollViewMarginBottom }}
/>,
<InputAccessory key={2} onLayout={...} />,
<KeyboardListener
key={3}
onWillShow={() => this.setState({ scrollViewMarginBottom: inputAccessoryHeight });
onWillHide={() => this.setState({ scrollViewMarginBottom: 0 })
/>
]
I was facing the same issue and reading online I figured out the following solution
For Android
Go to your AndroidManifest.xml and add android:windowSoftInputMode="adjustPan"
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustPan">
.....
</activity>
For IOS
Just follow the instructions in this repo.
https://github.com/douglasjunior/react-native-keyboard-manager.
Hope this helps. :)
I am very a beginner at Javascript, ES6 and ReactNative. I have a class component called Input. there I have a method (handleTextLayout) in which I get the width of my Text. I need to use that number inside the render method, let's say i want to show how big the text it. How can I have a class variable and the then change it in that method and pass it in other places in the class? Below is what I tried (besides many other things) but it doesn't work.
class Input extends Component {
constructor(){
super();
Input.TextWidth = '11111';
}
handleTextLayout(evt){
Input.TextWidth = evt.nativeEvent.layout.width;
}
render(){
const { label, placeholder, onChangeText, value } = this.props;
const { inputStyle, labelStyle, viewContainerStyle } = myInputStyle ;
return(
<View style={ viewContainerStyle } >
<Text style={ labelStyle } onLayout={this.handleTextLayout.bind(this)}>
{ Input.TextWidth }
</Text>
<TextInput
style={ inputStyle }
placeholder={ placeholder }
onChangeText={ onChangeText }
value={ value }
autoCorrect={false}
/>
</View>
);
};
};
it still shows that 11111 in the Text.
EDIT
Some folks in comments pointed out mistakes in this code - I fixed those now, thanks!
I don't know anything about React Native but I assume this works the same as in React.
I guess what you could do is to create a state and save that width there.
Then in the handleTextLayout method you set state to new value.
so:
constructor(props){
super(props);
this.state = {
textWidth: '11111'
};
this.handleTextLayout.bind(this); // a good practice
}
and then
handleTextLayout(evt){
this.setState({ textWidth: evt.nativeEvent.layout.width });
}
and finally in render:
<Text style={ labelStyle } onLayout={this.handleTextLayout}>
{ this.state.textWidth }
</Text>
This will work because setState will re-render the component so it shows new value.