I am receiving null is not an object (evaluating 'this.state.search') I am new to react-native so not 100% sure whats going on. thanks for any help.
This is my basic search bar:
use strict';
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
import renderIf from '../common/renderIf';
import { Container, Header, Title, Content, Icon, CardItem, Card, Input, InputGroup} from 'native-base';
export default class SearchBar extends Component {
render() {
return (
<Card>
<CardItem searchBar rounded>
<InputGroup>
<Icon name="ios-search" />
<Input placeholder="Search" value={this.state.search} onChangeText={(text) => this.setState({search:text})} onSubmitEditing={()=>this.search()}/>
</InputGroup>
</CardItem>
</Card>
);
}
}
This is is to take the inputed text from the search bar and display the results:
'use strict';
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
import renderIf from '../common/renderIf';
import { Container, Content, Icon, CardItem, Card, Thumbnail, Title, List, ListItem} from 'native-base';
import { Col, Row, Grid } from "react-native-easy-grid";
export default class AddMovieResults extends Component {
search() {
// Set loading to true when the search starts to display a Spinner
this.setState({
loading: true
});
var that = this;
return fetch('http://www.omdbapi.com/?s=' +this.state.search)
.then((response) => response.json())
.then((responseJson) => {
// Store the results in the state variable results and set loading to
// false to remove the spinner and display the list of repositories
that.setState({
results: responseJson,
loading: false
});
return responseJson.Search;
})
.catch((error) => {
that.setState({
loading: false
});
console.error(error);
});
}
render() {
return (
<Card scrollEnabled={true}>
<CardItem button >
<List dataArray={this.state.results.items} renderRow={(item) =>
<ListItem button >
<Row>
<Col size={1}>
<Thumbnail square style={{ height: 90, width:60, bottom:6,justifyContent: 'center',}} source={{uri: item.poster}} />
</Col>
<Col size={3}>
<Row size={3}>
<Text style={{ fontSize: 25, color: '#DD5044',justifyContent: 'center',}}>{item.title}</Text>
</Row>
<Row size={1}>
<Text style={{ fontSize: 15, color: '#DD5044',}}>{item._year_data}</Text>
</Row>
</Col>
</Row>
</ListItem>
} />
</CardItem>
</Card>
);
}
}
This is my index file which displays the above files on one page:
'use strict';
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
import renderIf from '../common/renderIf';
import { Container, Header, Title, Content} from 'native-base';
import { Col, Row, Grid } from "react-native-easy-grid";
import AddMovieResults from './AddMovieResults';
import SearchBar from './SearchBar';
export default class AddNewMovie extends Component {
render() {
return (
<Container scrollEnabled={false}>
<Header>
<Title> Add Movie</Title>
</Header>
<Content>
<Grid>
<Col>
{/* import search bar */}
<SearchBar/>
{/*import search results*/}
<AddMovieResults/>
</Col>
</Grid>
</Content>
</Container>
);
}
}
State is not global, it is local to each component so you will need to pass it to the object as a prop.
The problem here however is that as you are defining the search bar and add movie results you will need to find a way to pass the state back from SearchBar.
To accomplish this you can pass a reference function to update the state of AddNewMovie:
Add the following function to your addNewMovie class:
updateAddNewMovieState = (newData) => {
this.setState({search:newData})
}
Next pass it into the search bar class:
<SearchBar
updateState = {this.updateAddNewMovieState}
currentState = {this.state.search}
/>
Now use this.props.currentState to access the search state and this.props.updateState(newState) to modify the state in AddNewMovie from the search bar class.
Finally, pass the variable through to AddMovieResults:
<AddMovieResults
search={this.state.search}
/>
You can then access the variable in AddMovieResults via this.props.search.
While this method is relatively straight forward it quickly becomes convoluted if you are passing many variables around, for this purpose I recommend https://github.com/reactjs/react-redux which allows you to store variables much cleaner through action functions and reduction states.
I would also recommend defining your state variables in each component constructor to make it much clearer where they are being defined:
constructor(props) {
super(props);
this.state = {
something: "Something"
};
}
you need to bind your function in the constructor to be able to access this outside of inherited React class functions like render, constructor, etc...:
export default class AddMovieResults extends Component {
constructor(props){
super(props);
this.search = this.search.bind(this);
}
search() {
// Set loading to true when the search starts to display a Spinner
this.setState({
loading: true
});
}
...
...
}
Related
Parent Component--
import React from "react";
import {View,SafeAreaView,Text,StyleSheet} from 'react-native';
import TestComponent from "./Components/TestComponent";
const Test = () => {
return (
<View>
<Text>Here are some boxes of different colours</Text>
<TestComponent
color='#2aa198'
name='Cyan'
/>
<TestComponent
color='#268bd2'
name='Blue'
/>
<TestComponent
color='#d33682'
name='Magenta'
/>
<TestComponent
color='#cb4b16'
name='Orange'
/>
</View>
);
};
const styles = StyleSheet.create({
});
export default Test;
Child Component--enter code here
import React from "react";
import {View,Text,StyleSheet} from 'react-native';
const TestComponent = ({color,name}) => {
const colour = {
backgroundColor:color,
};
return (
<View style={colour}>
<Text>{name}{colour}</Text>
</View>
)
};
const styles = StyleSheet.create({
mH:{
marginHorizontal:10
},
mV:{
marginVertical:5
},
height:{
height:30
}
});
export default TestComponent;
When I try to use color prop as object in view in child property I'm getting error as "Objects are not valid as React child(found Object with keys {backgroundColor} and I also tried using colour.backgroundColor as well and still it is not working and I'm getting same error
You pass an object as a child to the Text component. I am assuming that you want to show the color code as a text inside the text component. Instead of passing the object colour, just pass color.
const TestComponent = ({color,name}) => {
const colour = {
backgroundColor:color,
};
return (
<View style={colour}>
<Text>{name}{color}</Text>
</View>
)
};
Here is a working snack.
Am new to the React world , so am facing issues with communications between the components. We have a 3 step login screen , and each screen has a top banner image and main background image , also few links in the footer .and the only difference is that in each screen we will have different form fields. lets say in the first screen we will have user name and a button , on the second screen some label and a text fields , and in the last screen we will have password fields and a image .
So what am planning is that i want to create a Index component with the common elements like i said the header logo, and the main image , and the footer . and i want to pass the components of the 3 screens into that so that Index will render the passed components , so if i pass the first component it should display the header logo , main image ,footer and the first screen component .
This is the first component which is calling Index and passing the LoginForm ,but its not rendering the passed LoginForm component, it just display the images of the Index component only.
How can i display the passed LoginForm within the Index component ?
import { LoginForm } from "./shared-components/LoginForm";
import { Index } from "./shared-components/Index";
export class Login extends Component {
constructor(prop) {
super(prop);
this.state = { username: "", password: "", result: [], isLoading: false };
}
render() {
return <Index passedForm={LoginForm} />;
}
}
This is the Index component
import React from "react";
import { Image, ImageBackground } from "react-native";
import { Container, Content, View } from "native-base";
export class Index extends React.Component {
constructor(prop) {
super(prop);
this.state = { username: "", password: "", result: [], isLoading: false };
}
render() {
return (
<Container>
<Content>
<Image
source={require("../assets/finastra_logo.jpg")}
style={{
maxHeight: 150,
alignSelf: "center",
maxWidth: 215,
flex: 1
}}
/>
<ImageBackground
resizeMode="cover"
blurRadius={3}
source={require("../assets/frond.jpg")}
style={{ alignSelf: "stretch", height: 500, marginBottom: 20 }}
>
{this.props.passedForm}
</ImageBackground>
</Content>
</Container>
);
}
}
You could fix it by passing the <LoginForm /> component instead of LoginForm.
But this is a good use case for the children property:
In Login.js, you can render:
render() {
return (
<Index>
<LoginForm />
</Index>
);
}
Then you can display the form in Index.js using:
{this.props.children}
You can pass it to variable and then use that variable to render
const PassedForm = this.props.passedForm;
And then render it like you would usually render components
<PassedForm />
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 3 years ago.
I have a react application and I'm trying to build a Navbar component using data from a javascript file.
My NavbarData.js file looks like the following:
const NavbarData = [
{
id: 1,
text: "Typography"
},
{
id: 2,
text: "Buttons"
},
{
id: 3,
text: "icons"
}
]
export default NavbarData
I'm using .map() to iterate over this data and create NavbarItem components inside my App.js file.
// Build navmenu items
const navbarItems = this.state.navbarData.map(function(item){
return <NavbarItem key={item.id} text={item.text} id={item.id}></NavbarItem>
});
And here is my NavbarItem.js file
import React, { Component } from 'react';
class NavbarItem extends Component{
render(){
return(
<>
<li key={this.props.id} id={this.props.id}>{this.props.text}</li>
</>
)
}
}
export default NavbarItem
All of this gives me something that looks like this. Which is great.
But I want to add a click listener to each of these. As this is a single page application, I would like to render either a typography, buttons, or icons component. To do this, I need a function that will update the state of the parent component which in my case is just App.js
So I put the following function inside App.js
//This function changes the state so that different components can render
navClick(id) {
console.log('changed', id);
}
And I made sure to bind it in my constructor of App.js
this.navClick = this.navClick.bind(this);
My entire App.js file now looks like this
//React stuff
import React, { Component } from 'react';
//Bootstrap stuff
import { Container, Row, Col } from 'reactstrap';
//Layout
import NavbarItem from './layout/NavbarItem'
import NavbarData from './layout/NavbarData'
//Components
import Typography from './components/Typography/Typography'
import Buttons from './components/Buttons/Buttons'
//Styles
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
class App extends Component {
constructor(){
super();
// State determines what component is active and loads navbar data
this.state = {
navbarData: NavbarData,
typography: true,
buttons: false,
icons: false
}
this.navClick = this.navClick.bind(this);
}
//This function changes the state so that different components can render
navClick(id) {
console.log('changed', id);
}
render() {
// Build navmenu items
const navbarItems = this.state.navbarData.map(function(item){
return <NavbarItem key={item.id} text={item.text} id={item.id}></NavbarItem>
});
// Determine what component to display in main area using state
let elementToDisplay;
if(this.state.typography){
elementToDisplay = <Typography></Typography>
}
else if(this.state.buttons){
elementToDisplay = <Buttons></Buttons>
}
////////////////////////////////////////////////////
return (
<Container fluid={true}>
<Row>
<Col>Header</Col>
</Row>
<Row>
<Col xs="12" sm="12" md="1" lg="1" xl="1">
<ul>
{navbarItems}
</ul>
</Col>
<Col xs="12" sm="12" md="11" lg="11" xl="11">
{elementToDisplay}
</Col>
</Row>
<Row>
<Col>Footer</Col>
</Row>
</Container>
);
}
}
export default App;
The problem comes when I try to attach the navClick function to the mapped NavbarItem like so.
// Build navmenu items
const navbarItems = this.state.navbarData.map(function(item){
return <NavbarItem navigationWhenClicked={this.navClick} key={item.id} text={item.text} id={item.id}></NavbarItem>
});
The error I receive is the following:
TypeError: this is undefined
When googleing this issue, this is the top post.
React: "this" is undefined inside a component function
But that's not my problem as I am making sure to bind my function.
I really have no idea what I'm doing wrong here. Any help would be appreciated.
The function you pass to .map also has its own this binding. The simplest solution is to pass this as second argument to .map:
const navbarItems = this.state.navbarData.map(function(item) {
...
}, this);
this inside the function will be set to whatever you pass as second argument, which in this case is the component instance.
Alternatively you can use an arrow function instead of a function expression, since this is resolved lexically (i.e. like any other variabe) inside arrow functions:
const navbarItems = this.state.navbarData.map(
item => <NavbarItem navigationWhenClicked={this.navClick} key={item.id} text={item.text} id={item.id} />
});
See also: How to access the correct `this` inside a callback?
I have saved one data inside one class of my react-native project. With saving data I start a new screen. In that screen I am retrieving that saved data. For that purpose, I call AsyncStorage.getItem('token') function inside the render function.
Here is the code for that class-
import React from 'react';
import { StyleSheet, Text, View, Image, AsyncStorage } from 'react-native';
import {Icon, Button, Container, Header, Content, Left} from 'native-base';
import CustomHeader from './CustomHeader';
class NoteMeHome extends React.Component {
state = {
text:'',
storedValue:'',
getValue: ''
};
static navigationOptions = ({navigation}) => ({
title: "Home",
headerLeft: <Icon name="ios-menu" style={{paddingLeft:10}}
onPress={()=>navigation.navigate('DrawerOpen')}/>,
drawerIcon:
<Image source={require('../assets/icon.png')}
style={styles.icon}
/>
})
render() {
const {storedValue} = this.state;
AsyncStorage.getItem('token').then(value =>
//AsyncStorage returns a promise so adding a callback to get the value
this.setState({ getValue: value })
//Setting the value in Text
);
return(
<Container>
<CustomHeader
title="Home"
drawerOpen={()=>this.props.navigation.navigate('DrawerOpen')}
/>
<Content contentContainerStyle={{flex:1, alignItems:'center',
justifyContent:'center', padding:10}}>
<Button full onPress={()=> this.props.navigation.navigate('Settings')}>
<Text style={{color:'white'}}>{storedValue}</Text>
</Button>
<Text>{this.state.getValue}</Text>
</Content>
</Container>
)
}
}
const styles = StyleSheet.create({
icon:{
height: 24,
width: 24
}
})
export default NoteMeHome;
After that, while running the project, in the above mentioned class, when I click any of my drawer items to go to another screen, it shows the following error in the console-
wanrning: can't call setState(or forceUpdate) on an unmounted
component. This is no-op, but it indicates a memory leak in your
application. To fix, cancel all subscriptions and asyncshronous tasks
in the componentWillUnmount method.
I guess something goes wrong with AsyncStorage.getItem('token') function calling because if I remove the function it doesn't show any warning.
So, it would be very nice if someone helps me to know where should I call the following code-
AsyncStorage.getItem('token').then(value =>
//AsyncStorage returns a promise so adding a callback to get the value
this.setState({ getValue: value })
//Setting the value in Text
);
to remove the warning ?
Asif,
Here is what I had in mind :
import React from 'react';
import { StyleSheet, Text, View, Image, AsyncStorage } from 'react-native';
import {Icon, Button, Container, Header, Content, Left} from 'native-base';
import CustomHeader from './CustomHeader';
class NoteMeHome extends React.PureComponent {
state = {
text:'',
storedValue:'',
getValue: ''
};
static navigationOptions = ({navigation}) => ({
title: "Home",
headerLeft: <Icon name="ios-menu" style={{paddingLeft:10}}
onPress={()=>navigation.navigate('DrawerOpen')}/>,
drawerIcon:
<Image source={require('../assets/icon.png')}
style={styles.icon}
/>
});
componentDidMount() {
const token = await AsyncStorage.getItem('token');
this.setState({ getValue: token });
}
render() {
const {storedValue, getValue} = this.state;
return(
<Container>
<CustomHeader
title="Home"
drawerOpen={()=>this.props.navigation.navigate('DrawerOpen')}
/>
<Content contentContainerStyle={{flex:1, alignItems:'center',
justifyContent:'center', padding:10}}>
<Button full onPress={()=> this.props.navigation.navigate('Settings')}>
<Text style={{color:'white'}}>{storedValue}</Text>
</Button>
<Text>{getValue}</Text>
</Content>
</Container>
)
}
}
const styles = StyleSheet.create({
icon:{
height: 24,
width: 24
}
})
export default NoteMeHome;
I don't know if in your case you should actually try to handle the update of the component since you don't have any props.
Regards,
IMO you should use componentDidMount for anything that you want to do in the beginning of a screen. To use AsyncStorage remember that is an asynchronous function so you have to wait for the function to complete so you can get the value.
For more information about react native's components please see this
For more information about 'waiting' for AsyncStorage using async please see this examples
In react component render() should always remain pure. One should never set state in render function, it a very bad pracice, in a simple component it might work fine. It only works because of the asynchronicity.
You should use componentDidMount lifecycle method to fetch data from local storage.
componentDidMount() {
const token = await AsyncStorage.getItem('token');
this.setState({ getValue: token });
}
I'm trying to pull a children element from a collection but I'm not able to, it doesn't seem to find the properties, I know I'm close to get what I want but I'm stuck on this. I'm using meteor 1.6 with React 16
this is my code:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Col, Thumbnail, Button, Modal, Row, Form, FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
import { Medicamento } from '../api/medicamento.js';
import PropTypes from 'prop-types';
export default class ListMeds extends Component {
constructor(props){
super(props);
}
render(){
function RenderLotes(){
const lotesArray = this.props.meds.Lotes.map((lista) =>
// The console indicates Cannot read property 'meds' of undefined
<div key={lista.codigoLote}>
<h3>{lista.codigoLote}</h3>
</div>
);
return(
<div>{lotesArray}</div>
);
}//function RenderLotes ends here
return(
<div>
<Col md={4}>
<div align="center"><strong>{this.props.meds.name}</strong></div>
<div align="center"><em>{this.props.meds.principio}</em></div>
</Col>
//the two lines above work fine, but crashes when I try to render the function
<Col md={4}>
<div><RenderLotes/></div>
</Col>
);
}
}
ListMeds.propTypes = {
meds: PropTypes.object.isRequired,
};
The this in RenderLotes is not the same this as in the outer render() function.. But you probably don't even need RenderLotes like that, just make the array like Andrew Kim suggested.. Or, if you really wanted to have it, something like this should work:
function RenderLotes(props) {
const lotesArray = props.meds.Lotes.map((lista) =>
<div key={lista.codigoLote}>
<h3>{lista.codigoLote}</h3>
</div>
);
return(
<div>{lotesArray}</div>
);
}
// later:
<div><RenderLotes meds={this.props.meds} /></div>
As the console error says, this.props is undefined within your RenderLotes() function, suggesting that this is a scoping issue; the this in your RenderLotes() does not refer to the React Component, but to something else.
One option would be to define the RenderLotes() function outside of the render() function, declaring both functions at the same level :
RenderLotes() {...}
render() {...}
Then, in your JSX, call it this way:
<div>{this.RenderLotes()}</div>
Put your props in state when in your constructor
export default class ListMeds extends Component {
constructor(props){
super(props);
this.state = {
meds = props.meds
}
}
render(){
return (
{this.state.meds.map(lista => {
return (
<div key={lista.codigoLote}>
<h3>{lista.codigoLote}</h3>
</div>
)
})}
)
}
And make sure when you render that you are actually passing a prop called meds
ReactDOM.render(<ListMeds meds={meds} />, document.getElementById('root'))
I'm not entirely sure what your question is, but your code should look probably more like this:
render(){
let lotesArray = this.props.meds.Lotes.map((lista) => {
return(
<div key={lista.codigoLote}>
<h3>{lista.codigoLote}</h3>
</div>
)
})
return (
<div>
<Col md={4}>
<div align="center"><strong>{this.props.meds.name}</strong></div>
<div align="center"><em>{this.props.meds.principio}</em></div>
</Col>
<Col md={4}>
<div>{lotesArray}</div>
</Col>
</div>
)
}
First of all if console says that meds property is undefined it means that it doesn't exist hence you cannot iterate over it. Second thing is how you invoke RenderLotes function. <div><RenderLotes/></div> this won't work. Instead you need to change it to <div>{this.RenderLotes()}</div>. And move RenderLotes function declaration out of render method. Also you don't close div tag in render function. Change the code to something like that:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { Col, Thumbnail, Button, Modal, Row, Form, FormGroup, FormControl, ControlLabel } from 'react-bootstrap'
import { Medicamento } from '../api/medicamento.js'
import PropTypes from 'prop-types'
export default class ListMeds extends Component {
renderLotes = () => this.props.meds.Lotes.map(lista =>
(<div key={lista.codigoLote}>
<h3>{lista.codigoLote}</h3>
</div>))
render() {
return (
<div>
<Col md={4}>
<div align="center"><strong>{this.props.meds.name}</strong></div>
<div align="center"><em>{this.props.meds.principio}</em></div>
</Col>
<Col md={4}>
<div>{!!this.props.meds && this.renderLotes()}</div>
</Col>
</div>
)
}
}
ListMeds.propTypes = {
meds: PropTypes.object.isRequired,
}