Why my bind event did not invoke in Wechat little-program? - wechat

This is my code below:
search.wxml:
<view class="search">
<view class="section">
<input id="input-word" placeholder="请输入您要查询的文本" focus/>
<view id="cancel-button" bindTap="navigateBack" class="navigator" >取消</view>
</view>
</view>
search.js:
Page({
onLoad: function () {
console.log('onLoad-search')
},
// back
navigateBack: function(e) {
console.log("back"); // this do not log out, when I click my view
}
})

The position of the function "navigateBack" is wrong.You should put it like this:
navigateBack: function(e) {
console.log("back"); // this do not log out, when I click my view
}
onload: function() {
console.log('onLoad-search')
}

Related

React Native Firebase - State Not Able to be Retrieved on First Load

I'm using useEffect to retrieve some user and group data on initial screen load on a react-native app. The following code for this is here:
const [groupInfo, setGroupInfo] = useState([]);
//Called on INITIAL rendering
useEffect(() => {
async function getGroupData() {
let groupCode = '';
//Retrieve group code from user
await getDoc(doc(db, 'users', email)).then(userSnapshot => {
if (userSnapshot.exists()) {
groupCode = userSnapshot.data()['group_code'];
}
else { console.log('No user with that email exists!'); }
}).catch(err => {
console.log(err);
});
//Retrieve group information from user
await getDoc(doc(db, 'groups', groupCode)).then(groupSnapshot => {
if (groupSnapshot.exists()) {
setGroupInfo(groupSnapshot.data());
}
else { console.log('No group with that code exists!'); }
}).catch(err => {
console.log(err);
});
}
getGroupData();
}, [email]);
The problem is that when I've tried to render this on my return statement, I get an error. I've logged my data before and that has worked fine but it seems that the app loads the view first. THe following react render code and error are below:
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerLeft}>Goals</Text>
<Text style={styles.headerRight}>Week of 11/20/22</Text>
</View>
<Text style={styles.prize}>Prize: Winner gets a free starbucks drink!</Text>
{/*<View style={styles.goalBox}>
<Text>Workout 3x per week</Text>
<CheckBox style={styles.checkbox}/>
</View> */}
{/* TODO edit prize screen */}
<Button title="Edit prize" />
<View style={styles.memberHeader}>
<Text style={styles.headerLeft}>Members</Text>
<Text style={styles.numMembers}>4</Text>
</View>
{
groupInfo['members'].map((memberName, index) =>
<View style={styles.member}>
<Text style={styles.memberText}>{memberName}</Text>
</View>
)
}
<Button title="Invite member" />
</View>
);
Error:
ERROR TypeError: Cannot read property 'map' of undefined
This error is located at:
in ScreenViewGroup (created by SceneView)
EDIT:
So I think the problem is that at the same time the component is being rendered, the data is being loaded in. I'm still receiving the same error but I've noticed that when I edit my code, the data gets loaded in automatically.
Text strings also seem to render in properly as well but just not the array.
So I found a work-around. By declaring my initial array with values, React will fill those values in as default values and immediately change them to the firebase values when they load.

this.props.route.params returns value as undefined

I'm building a barcode reader app that scans that qr code and then takes data and is used as a key to fetch an object from firebase. In order the data to be used as a key I need to pass through another screen but when I check console log it's cameback that the scanned key is undefined.
The itself barcode scanner works perfectly.
Barcode class :
export class BarCodeScannerScreen extends Component{
state = {
CameraPermissionGranted: null,
}
async componentDidMount() {
// Ask for camera permission
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ CameraPermissionGranted: status === "granted" ? true : false });
};
barCodeScanned = ({ data }) => {
//Access the Data
alert(data); // shows the scanned key
this.props.navigation.navigate('Info', {
item: data, }); // but then it's dissapears in here.
};
render(){
const { CameraPermissionGranted } = this.state;
if(CameraPermissionGranted === null){
// Request Permission
return(
<View style={styles.container}>
<Text>Please grant Camera permission</Text>
</View>
);
}
if(CameraPermissionGranted === false){
// Permission denied
return (
<View style={styles.container}>
<Text>Camera Permission Denied.</Text>
</View>
);
}
if(CameraPermissionGranted === true){
// Got the permission, time to scan
return (
<View style = {{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<BarCodeScanner
onBarCodeScanned = {this.barCodeScanned }
style = {{
height: DEVICE_HEIGHT/1.1,
width: DEVICE_WIDTH,
}}
>
</BarCodeScanner>
</View>
);
}
}
}
Here is my Info screen that receives the information :
export default class InfoScreen extends Component {
constructor(props){
super(props);
this.state={
productlist:[],
scannedkey: this.props.route.params.item
} }
async componentDidMount(){
firebase.database().ref(`product/${ this.state.scannedkey}`).on(
"value",
(snapshot) => {
var list = [];
snapshot.forEach((child) => {
list.push({
key: child.key,
title: child.val().title,
//details: child.val().details,
//price: child.val().price
});
});
this.setState({ productlist: list });
},
(error) => console.error(error)
);
}
componentWillUnmount() {
if (this.valuelistener_) {
this.valueRef_.off("value", this.valuelistener_)
}}
render() {
console.log(this.state.scannedkey); // console log shows that scanned key is undefined
return(
<View style={styles.container}>
<Text>Hey</Text>
<Text>{this.state.productlist.title}</Text>
</View>
);}}
App.js
export default function App() {
const Drawer=createDrawerNavigator();
return (
<Provider store={store}>
<NavigationContainer>
<Drawer.Navigator initialRouteName="Barcode">
<Drawer.Screen name="Barcode" component={BarCodeScannerScreen} />
<Drawer.Screen name="Info" component={InfoScreen} />
</Drawer.Navigator>
</NavigationContainer>
</Provider>
);
}
I ussualy use function components to navigate through but with class components it's a little tricky for me. Perhaps I missed something?
So far I 've tried :
this.props.navigation.navigate('Info', {
item: JSON.stringify(data) , });
And it didn't work.
I will be grateful for your help.
Try to use item directly from props, not from state
in your componentDidMount call where you supply from state the scannedKey, supply it from props
firebase.database().ref(`product/${this.props.route.params.item}`)....
you are also calling this.props instead of props directly in your state inside your constructor, which have direct access to it, that's why you can call super(props) and not super(this.props), I am not sure if this is the issue, but in react docs says don't copy props to state because they get ignored, and it's bad practice my friend.
check this link, in the big yellow note what I am reffering to
https://reactjs.org/docs/react-component.html#constructor

react native error: "nothing was returned from render"

This is my sign out function which should redirect the user to the login screen after logout. But it is not working and I am getting the error "nothing was returned from this render".
const signout= async() => {
setShowLoading(true);
try {
const a= await auth().signOut().then(()=>{
console.log(a);
setUser(null)
setShowLoading(true)
if(!user)
{
return navigation.navigate('Login')
}
}
);
}
catch (e) {
setShowLoading(false);
Alert.alert(
e.message
);
}
};
and this is my return in function
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Welcome {user.email}</Text>
<Button title="logout" onPress={()=>signout()}/>
{showLoading &&
<View style={styles.activity}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
}
</View>
);
Nothing was returned from render function means, that one of you Components has nothing returned to it to render. In your codeblock's case, in a case where there is user, nothing is being returned. Either you edit renderMethod to return Home screen or you can navigate user to a different page before user is set or unset depending on your requirement. Either way, react must have something to render to the screen or it will throw an error like this.

Rendering Admob Banner within react native flatlist items

I have integrated react-native-firebase and the AdMob module into my react native project and everything is working as expected (test ads and ads in production). I now want to add banners within my Flatlist (rendering a banner every X number of rows).
this is an example of the data I'm receiving from the server:
[
{
...item 1 details
},{
...item 2 details
},{
...ad details
},{
...item 3 details
},{
...item 4 details
},{
...ad details
}
]
and this is the Flatlist component
class ItemsList extends React.Component {
_renderItem = ({item, index}) => {
if (item.isAd) {
const unitId = Platform.OS === "ios" ? item.adIdIOS : item.adIdAndroid;
const Banner = firebase.admob.Banner;
const AdRequest = firebase.admob.AdRequest;
const request = new AdRequest();
const keyWords = item.admobKeywords || [];
keyWords.forEach(keyword => {
request.addKeyword(keyword);
});
return (
<View style={{ width:"100%", height: item.adHeight || "auto", marginTop: 5, marginBottom: 15, alignItems: 'center', justifyContent: 'center' }}>
<Banner
size={item.adSize}
request={request.build()}
unitId={unitId}
onAdLoaded={() => {}}
/>
</View>
)
} else {
renderItem()
}
}
render() {
return(
<View>
<FlatList
data={this.state.data}
renderItem={this._renderItem}
...
/>
</View>
)
};
};
the problem is that whenever a scroll event happens (user scrolls down), the Flatlist keeps re-rendering the banner (firing a new request) and the banner keeps refreshing. After a while, a lot of requests get fired and the application crashes!
I know that calling a network request inside a renderItem method is not a good practice so I also tried the following but the problem is still the same:
const Banner = firebase.admob.Banner;
const AdRequest = firebase.admob.AdRequest;
const request = new AdRequest();
class CatalogueSalesList extends React.Component {
_renderItem = ({item, index}) => {
if (item.isAd) {
const unitId = Platform.OS === "ios" ? item.adIdIOS : item.adIdAndroid;
const keyWords = item.admobKeywords || [];
keyWords.forEach(keyword => {
request.addKeyword(keyword);
});
return (
<View style={{ width:"100%", height: item.adHeight || "auto", marginTop: 5, marginBottom: 15, alignItems: 'center', justifyContent: 'center' }}>
<Banner
size={item.adSize}
request={request.build()}
unitId={unitId}
onAdLoaded={() => {}}
/>
</View>
)
} else {
renderItem()
}
}
render() {
return(
<View>
<FlatList
data={this.state.data}
renderItem={this._renderItem}
...
/>
</View>
)
};
};
Can someone please provide me with a solution to how a banner ad could be implemented inside a Flatlist without the banner getting re-rendered every time the user scrolls through the list?
the version that I'm using:
"react-native": "0.59.9",
"react-native-firebase": "~5.5.3"
Flatlist component contains onScroll event hooks you can use to determine when and what type of scrolling is happening:
onScrollBeginDrag, onScrollEndDrag,onMomentumScrollBegin, onMomentumScrollEnd
Use these and make your banner component/list item a stateful component.
The code below will render your admob stuff (and it's associated network requests) only when user is not scrolling and only once.
If your banner component is straight from admob, then wrap it in a stateful component and do the same as below.
List Parent Component
class CatalogueSalesList extends React.Component {
constructor() {
this.state = {
isDragging: false,
isGliding: false,
}
_renderItem = ({item, index}) => {
return (
<View>
<Banner
isDragging={this.state.isDragging}
isGliding={this.state.isGliding}
size={item.adSize}
request={request.build()}
unitId={unitId}
onAdLoaded={() => {}}
/>
</View>
)
} else {
renderItem()
}
}
render() {
return(
<View>
<FlatList
onScrollBeginDrag={
() => {this.setState({isDragging: true});}
}
onScrollEndDrag={
() => {this.setState({isDragging: false});}
}
onMomentumScrollBegin={
() => {this.setState({isGliding: true});}
}
onMomentumScrollEnd={
() => {this.setState({isGliding: false});}
}
data={this.state.data}
renderItem={this._renderItem}
...
/>
</View>
)
};
};
Banner.tsx
class Banner extends React.Component {
constructor(props) {
super(props)
this.state = {
shouldRenderAd: false,
}
}
componentDidMount() {
if (!this.props.isGliding && !this.props.isDragging) {
this.setState({
shouldRenderAd: true,
});
}
}
shouldComponentUpdate(nextProps: Props): boolean {
if (this.state.shouldRenderAd) {
// if it has already rendered the ad, don't re-render
return false;
} else if (!nextProps.isDragging && !nextProps.isGliding) {
// if stationary, ok to re-render
return true;
} else if (nextProps.isDragging && !nextProps.isGliding) {
// if dragging, but not gliding, ok to re-render
return true;
} else {
// otherwise (it is gliding) don't re-render
return false;
}
}
componentDidUpdate() {
if (!this.props.isGliding && !this.props.isDragging) {
// if no scrolling is happening
this.setState({
shouldRenderAd: true,
});
}
}
render() {
if (this.state.shouldRenderAd) {
return (
// render your admob (network requests) stuff here
);
} else {
return (
// Placeholder component for your ad
// probably an empty view the same dimensions as the ad banner
);
}
}
}

react native firebase authentication

In react native with firebase I get a chat app code. It used a button to show my registered friends, but I want to check when this button click user is logged in or not.
This is the button code in render function
render() {
return (
<View style={styles.containerl}>
<StatusBar barStyle="light-content" backgroundColor="red" />
<TouchableOpacity>
<Text
style={styles.buttonStyle}
onPress={() => this.props.navigation.navigate("Friendlist")}
>
SHOW FRIEND LIST
</Text>
</TouchableOpacity>
</View>
);
}
I want to add this firebase authentication code in to Show Friend List text press.
firebase.auth().onAuthStateChanged((user) => {
if (user) {
console.log('user logged')
}
});
Can anyone help me?
You can do like below. Add a flag to check whether the user logged in or not
state = {
isUserLogged: false
}
constructor(props) {
super(props);
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.setState({
isUserLogged: true
})
} else {
this.setState({
isUserLogged: false
})
}
});
}
render() {
return (
<View style={styles.containerl}>
<StatusBar barStyle="light-content" backgroundColor="red" />
<TouchableOpacity>
<Text
style={styles.buttonStyle}
onPress={() => {
if (this.state.isUserLogged) {
this.props.navigation.navigate("Friendlist")
} else {
console.log('user not logged')
}
}
}
>
SHOW FRIEND LIST
</Text>
</TouchableOpacity>
</View>
);
}
In order to show whether a user is logged in or not, you'll need to attach a variable to the user state. So, your authentication code:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
console.log('user logged')
}
});
essentially, will put this method in either the constructor() or the componentDidMount() method.
From there, you'll need to set the state, like -
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.setState({ userLoggedIn: true });
console.log('user logged')
}
});
and in your render() you can attach conditions to this.state.userLoggedIn on your onPress() method accordingly.
If you need to send the logged in state to your <Friendlist/> component, you'll be required to do -
this.props.navigation.navigate("Friendlist", {userLoggedIn: this.state.userLoggedIn})
and in your <Friendlist/> component you can fetch it from this.props.navigation.state.params.userLoggedIn

Resources