i have index.js contain's Tabs each tab render the same component (animate.js) but with different props, the code as the following:
index.js
categoryList.map((item,index) => {
if(item.head_category == category_id)
return (
<Tab heading={item.category_name} key={index}>
<Animate category_id={item.category_id}/>
</Tab>
)
});
in the animate.js i receive the category_id number and fetch the data using redux , and the data gets back
for first animate.js rendering nothing fires after the data returns but if i switch the tabs everything works grate
animate.js
import React from 'react';
import { InteractionManager, StyleSheet, Text, View, Button } from 'react-native';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {LineDotsLoader} from 'react-native-indicator';
import {goodsAction} from './redux/actions'
class animate extends React.Component {
constructor(props) {
super(props);
this.state = {
category_id:'',
loading:true,
};
console.log('constructor');
}
componentDidMount(){
const { category_id, category_name } = this.props;
this.props.goodsAction({category_id}); // My fetching Action
console.log(`componentDidMount `);
}
componentWillReceiveProps(nextProps){
console.log(`componentWillReceiveProps`)
}
static getDerivedStateFromProps(props, state){
console.log(` getDerivedStateFromProps `);
return null;
}
componentDidUpdate(prevProps, prevState) {
console.log(` componentDidUpdate `);
}
renderPage() {
return (
<View style={{ flex: 1 }}>
// anything
</View>
);
}
render(props) {
console.log(`render`);
if (this.props.loading) {
// if redux still fetching return loading:true else return false
return(<View style={styles.container}><LineDotsLoader /></View>)}
return (
<View style={styles.container}>
{this.renderPage()} // or anything
</View>
);
}
}
const mapStateToProps = state => {
return {
error: state.goods.error,
loading: state.goods.loading,
goods: state.goods.goods
}
}
export default connect(mapStateToProps, { goodsAction })(animate);
my console image
Edit
and this is my goodsRedusers.js
import {
GOODS_LOADING_ATTEMPT,
GOODS_REFRESH_ATTEMPT,
GOODS_LOADED,
GOODS_FAILED
} from '../actions/types';
const INITIAL_STATE = { goods:[], loading: true, error: '', }
export default (state = INITIAL_STATE, action) => {
switch(action.type) {
case GOODS_LOADING_ATTEMPT://dispatch before connecting to db
return {...INITIAL_STATE, loading: true }
case GOODS_FAILED:
return {...INITIAL_STATE, loading: false, error: action.error }
case GOODS_LOADED://dispatch after data gets back
return {...INITIAL_STATE, loading: false, goods: action.goods }
default:
return state;
}
}
static getDerivedStateFromProps(props, state){
console.log(` getDerivedStateFromProps `);
return null;
}
Returning null won't update the state when props change, if that's what you want.
Try removing this code.
Related
I'am trying to use react-native-image-picker function inside the reducer to change the avatar but the image is not changing.
i can solve this by writing the image picker function inside the avatar.js file but i want to use this way.
if anyone knows how to solve this problem please.
here is my code :
avatarReducer.js
import ImagePicker from 'react-native-image-picker';
const initialState = {avatar: require('../../Images/ic_tag_faces.png')};
function setAvatar(state = initialState, action) {
var nextState;
switch (action.type) {
case 'SET_AVATAR':
ImagePicker.showImagePicker({}, response => {
if (response.didCancel) {
console.log("L'utilisateur a annulé");
} else if (response.error) {
console.log('Erreur : ', response.error);
} else {
console.log('Photo : ', response.uri);
var requireSource = {uri: response.uri};
nextState = {
...state,
avatar: requireSource,
};
return nextState || state;
}
});
return state;
default:
return state;
}
} // end function
export default setAvatar;
Avatar.js
import React from 'react';
import {StyleSheet, Image, TouchableOpacity} from 'react-native';
import {connect} from 'react-redux';
class Avatar extends React.Component {
constructor(props) {
super(props);
}
_setAvatar() {
const action = {type: 'SET_AVATAR'};
this.props.dispatch(action);
}
render() {
return (
<TouchableOpacity
style={styles.touchableOpacity}
onPress={() => this._setAvatar()}>
<Image style={styles.avatar} source={this.props.avatar} />
</TouchableOpacity>
);
}
} // end class
const mapStateToProps = state => {
return {
avatar: state.setAvatar.avatar,
};
};
export default connect(mapStateToProps)(Avatar);
I installed the react-navigation package in react-native
I have implemented tab navigation and one of them is implemented in webview format.
My problem is that if I press the back physical button on Android, I go from the app itself to the previous tab, not back from the webview.
I've already applied the back button for the webview on the internet, but I have not done that.
I tried to display the onNavigationStateChange log when debugging, but it was not updated when url was moved after it was loaded at first startup. Here is the code I implemented:
import React from "react";
import {BackHandler} from "react-native";
import {WebView} from "react-native-webview";
class SermonScreen extends React.Component {
constructor(props) {
super(props);
}
static navigationOptions = {
header: null
};
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
}
_onNavigationStateChange(navState) {
console.log(navState);
this.setState({
canGoBack: navState.canGoBack
});
}
handleBackButton = () => {
console.log(this.state);
if (this.state.canGoBack === true) {
this.webView.goBack();
return true;
} else {
return false;
}
};
render() {
return (
<WebView
source={{uri: 'https://m.youtube.com/channel/UCw3kP3qCCF7ZpLUNzm_Q9Xw/videos' }}
ref={(webView) => this.webView = webView}
onNavigationStateChange={this._onNavigationStateChange.bind(this)}
/>
);
}
}
export default SermonScreen;
Following the official webview documnentation you could try to do this: https://github.com/react-native-community/react-native-webview/blob/master/docs/Guide.md#intercepting-hash-url-changes
In general you were almost there, however the way the YT navigation works made it impossible to be caught via the onNavigationStateChange, that's why we inject a JS code that intercepts these hash changes and posts a message to the parent component, we then catch it inside the onMessage handler and set the state variable properly. Copying the injectedJavaScript and onMessage properties to your example should solve your problem.
I prepared a component for you that seems to do what is needed:
* Sample React Native App
* https://github.com/facebook/react-native
*
* #format
* #flow
*/
import React, { Fragment } from "react";
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
BackHandler,
StatusBar
} from "react-native";
import { WebView } from "react-native-webview";
import {
Header,
LearnMoreLinks,
Colors,
DebugInstructions,
ReloadInstructions
} from "react-native/Libraries/NewAppScreen";
class App extends React.Component {
constructor(props) {
super(props);
this.startingUrl =
"https://m.youtube.com/channel/UCw3kP3qCCF7ZpLUNzm_Q9Xw/videos";
this.handleBackButton = this.handleBackButton.bind(this);
}
componentDidMount() {
BackHandler.addEventListener("hardwareBackPress", this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", this.handleBackButton);
}
handleBackButton = () => {
console.log(this.state);
const { canGoBack } = this.state;
if (canGoBack) {
this.webView.goBack();
return true;
} else {
return false;
}
};
render() {
return (
<Fragment>
<WebView
source={{ uri: this.startingUrl }}
style={{ marginTop: 20 }}
ref={webView => (this.webView = webView)}
injectedJavaScript={`
(function() {
function wrap(fn) {
return function wrapper() {
var res = fn.apply(this, arguments);
window.ReactNativeWebView.postMessage('navigationStateChange');
return res;
}
}
history.pushState = wrap(history.pushState);
history.replaceState = wrap(history.replaceState);
window.addEventListener('popstate', function() {
window.ReactNativeWebView.postMessage('navigationStateChange');
});
})();
true;
`}
onMessage={({ nativeEvent: state }) => {
if (state.data === "navigationStateChange") {
// Navigation state updated, can check state.canGoBack, etc.
this.setState({
canGoBack: state.canGoBack
});
}
}}
/>
</Fragment>
);
}
}
export default App;
The response above was perfect. I set the state true for canGoBack though; I was getting a null error, so:
constructor(props) {
super(props);
this.startingUrl = "https://app.vethorcardpag.com.br/GIF/login/0/";
this.state = {
canGoBack : true
}
this.handleBackButton = this.handleBackButton.bind(this);
}
Here is a simple solution using the magic of React's State.
Hope this helps.
import React, { useRef, useState } from 'react'
export default function Component () {
// This is used to save the reference of your webview, so you can control it
const webViewRef = useRef(null);
// This state saves whether your WebView can go back
const [webViewcanGoBack, setWebViewcanGoBack] = useState(false);
const goBack = () => {
// Getting the webview reference
const webView = webViewRef.current
if (webViewcanGoBack)
// Do stuff here if your webview can go back
else
// Do stuff here if your webview can't go back
}
return (
<WebView
source={{ uri: `Your URL` }}
ref={webViewRef}
javaScriptEnabled={true}
onLoadProgress={({ nativeEvent }) => {
// This function is called everytime your web view loads a page
// and here we change the state of can go back
setWebViewcanGoBack(nativeEvent.canGoBack)
}}
/>
)
}
Original answer
https://stackoverflow.com/a/74500469/7823800
I have to display a error message called "No record available".
This is my scenario : -
API Call {
if (data){
loading == false
}
}
In my component
Render(){
{
data.length > 0 && this.state.loading == false ?
<Flat List/>
: null
}
{
data.length==0 ?
<Text>No Record found</Text>
: null
}
}
My Problem was , my message displays if data not found but it doesn't refresh.
I have to achieve a scenario like this -
when i open or navigate through a page then its first show blank then loader start and after API call if data not found then they display a message.
This is a working example of what you describe. When the component loads the data is empty until your API call runs in componentDidMount. I have emulated the API call with a timeout of 2 seconds. You need to switch out the setTimeout function in apiCall with your own fetch method and set the state in the callback of that function
import React, { Component } from 'react';
import { View, Text } from 'react-native';
class Test extends Component {
state = {
loading: false,
data: [],
};
componentDidMount() {
this.apiCall();
}
apiCall = () => {
this.setState({ loading: true });
setTimeout(() => {
this.setState({
loading: false,
data: ['1', '2', '3'],
});
}, 3000);
};
render() {
if (this.state.loading) return <Text>Loading...</Text>;
if (this.state.data.length === 0) return <Text>No records found</Text>;
return (
<View>
<Text>Records found</Text>
</View>
);
}
}
export default Test;
You can bind your action and reducers data
here this is the example you want
import React, { Component } from 'react';
import {
Text,
View,
Dimensions,
FlatList,
ScrollView,
} from 'react-native';
import { connect } from 'react-redux';
import {showLoading, getProducts} from '../actions/productAction';
import * as Progress from 'react-native-progress';
class Data extends Component {
this.state = {
product: [],
loading: false
};
componentWillMount() {
this.setState({loading: true});
API CALL();
}
render() {
return (
<View>
{this.state.isLoading ?
<View>
<Progress.CircleSnail thickness={5} size={55} color={['#000000', '#000000', '#FFFFFF',]} />
</View>
:
null
}
{this.state.product.length === 0 && <View>
<Text>{"NO PRODUCT"}</Text>
</View>}
<FlatList
data={this.state.product}
/>
</View>
);
}
}
export default Data;
After my previous attempt failed, I ran git stash and started over, and the code inside return (dispatch) finally executed. Unfortunately, it began to execute twice, but still not populate the state (or by executing twice it overwrote the state).
Here is my code:
DivisionManagement\index.js
import React, {Component} from "react";
import {View, FlatList, Alert} from "react-native";
import {withNavigation} from "react-navigation";
import {connect} from "react-redux";
import PortableButton from "./../../components/PortableButton";
import masterStyles, {listPage, bigButtonStyles} from "./../../styles/master";
import DivisionManagementRow from "./DivisionManagementRow";
import { loadDivisions } from "../../redux/actions/divisionActions";
class DivisionManagmementScreen extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log(`Entered componentDidMount(), props: ${JSON.stringify(this.props)}`);
this.props.getAllDivisions(1);
console.log(`props again: ${JSON.stringify(this.props)}`);
}
_renderItem = ({item}) => {
console.log(`item: ${JSON.stringify(item)}`);
return (
<DivisionManagementRow divisionId={item.DIVISION_ID} divisionName={item.DIVISION_NAME}
onAddTeam={() => {this._addTeam(item.DIVISION_ID)}}
onEdit={() => {this._editDivision(item.DIVISION_ID)}}
onDelete={() => {this._btnDeleteDivision(item.DIVISION_ID)}}/>
)};
render() {
console.log(`Props in render: ${JSON.stringify(this.props)}`);
return (
<View style={masterStyles.component}>
<View style={listPage.listArea}>
<FlatList
data={this.props.divisions}
renderItem={this._renderItem}
keyExtractor={(item) => item.DIVISION_ID.toString() } />
</View>
<View style={listPage.bottomButtonArea}>
<PortableButton defaultLabel="Add Division"
disabled={false}
onPress={() => {this._addDivision()}}
onLongPress={() => {}}
style={bigButtonStyles} />
</View>
</View>
);
}
}
function mapStateToProps(state) {
console.log(`State: ${JSON.stringify(state)}`);
return {
programId: state.programId,
divisions: state.divisions,
isLoading: state.isLoadingDivisions
}
}
function mapDispatchToProps(dispatch) {
return {
getAllDivisions: (programId) => dispatch(loadDivisions(programId))
};
}
export default withNavigation(connect(mapStateToProps, mapDispatchToProps)(DivisionManagmementScreen));
divisionActions.js
import {query2} from "./../../util/DbUtils";
export const DIVISIONS_LOADING = "DIVISIONS_LOADING";
export const DIVISIONS_BY_PROGRAM = "DIVISIONS_BY_PROGRAM";
export const DIVISIONS_POPULATE = "DIVISIONS_POPULATE";
export function loadDivisions(programId) {
console.log(`loadDivisions, programId: ${JSON.stringify(programId)}`);
let result = (dispatch) => {
dispatch(isLoadingDivisions(true));
query2("SELECT * FROM DIVISION WHERE DIVISION_PROGRAM_ID = ?", [programId])
.then((queryResults) => {
console.log("Query results: " + JSON.stringify(queryResults));
return queryResults;
}).then((queryResults) => {
dispatch(isLoadingDivisions(false));
return queryResults;
}).then((queryResults) => {
console.log("Dispatching query results " + JSON.stringify(queryResults));
dispatch(populateDivisions(queryResults.result));
//return queryResults;
});
}
return result;
}
export function isLoadingDivisions(value) {
console.log(`Entered isLoadingDivisions, value: ${value}`);
return {
type: DIVISIONS_LOADING,
payload: value
}
}
export function getDivisionsByProgram(programId) {
return {
type: DIVISIONS_BY_PROGRAM,
payload: programId
}
}
export function populateDivisions(divisions) {
console.log(`populateDivisions(), divisions: ${JSON.stringify(divisions)}`);
return {
type: DIVISIONS_POPULATE,
payload: divisions
}
}
divisionReducer.js
import { DIVISIONS_LOADING, DIVISIONS_BY_PROGRAM } from "../actions/divisionActions";
import {loadDivisions} from "../actions/divisionActions";
export function isLoadingDivisions(state = false, action) {
console.log(`Entered isLoadingDivisions: ${JSON.stringify(action)}`);
switch (action.type) {
case DIVISIONS_LOADING:
return action.payload;
default:
return state;
}
}
export function divisions(state = [], action) {
console.log("Entered divisions()");
switch (action.type) {
case DIVISIONS_BY_PROGRAM:
let divisions = loadDivisions(action.payload);
console.log(`Divisions in divisions(): ${JSON.stringify(divisions)}`);
return divisions;
default:
return state;
}
}
reducers\index.js
import {combineReducers} from "redux";
import {isLoadingDivisions, divisions} from "./divisionReducer";
export default rootReducer = combineReducers({
isLoadingDivisions,
divisions
});
What should be happening here is that state should have an entry:
{ divisions: // populated array }
Instead, I'm getting this:
{ divisions: [] }
(The second one corresponds with the initial state, so maybe it's being overwritten?)
So I have one, or potentially two, problems with this code:
It's executing twice according to the logs
the divisions array in the state is not populating.
Can anyone help?
everyone, I have been reporting wrong when I used the react-native compilation project, I don't know how to solve it, I can't find the error, please give me some Suggestions, thank you very much.
import React, { PureComponent } from 'react';
import { BackHandler, Platform, View, StatusBar, Text,Modal } from 'react-native';
import {
addNavigationHelpers
} from 'react-navigation';
import { connect } from 'react-redux';
import moment from 'moment';
import SplashScreen from 'react-native-splash-screen';
import { loadToken, getNetConfig, saveNetConfig, loadNetConfig } from './dvapack/storage';
import { createAction, NavigationActions, getCurrentScreen } from './utils';
import NetConfig from './config/NetConfig.json';
import api from './config/globalapi';
import AppNavigator from './containers/';
*I don't know if this is the correct way of writing the router, and it has led to this problem.*
#connect(({ router }) => ({ router }))***//一直报这里的错误=I've been making mistakes here.***
class Router extends PureComponent {
constructor(props) {
super(props);
this.state = {
configload: true
};
}
async componentWillMount() {
let netconfig =await loadNetConfig();
if (!netconfig && !netconfig != null) {
if (NetConfig.isAutoLoad) {
const newconfig = [];
NetConfig.Config.map((item, key) => {
const netitem = {};
// netitem.neturl = `http://${item.configIp}:${item.configPort}`+api.appurl;
netitem.neturl = `http://${item.configIp}:${item.configPort}`;
if (key === 0) {
netitem.isuse = true;
} else {
netitem.isuse = false;
}
newconfig.push(netitem);
});
saveNetConfig(newconfig);
} else {
this.setState({ configload: false });
SplashScreen.hide();
}
}
BackHandler.addEventListener('hardwareBackPress', this.backHandle);
}
async componentDidMount() {
const user = await loadToken();
this.props.dispatch(createAction('app/loadglobalvariable')({ user }));
}
componentWillUnmount() {
if (Platform.OS === 'android') {
BackHandler.removeEventListener('hardwareBackPress', this.backHandle);
JPushModule.removeReceiveCustomMsgListener(receiveCustomMsgEvent);
JPushModule.removeReceiveNotificationListener(receiveNotificationEvent);
JPushModule.removeReceiveOpenNotificationListener(openNotificationEvent);
JPushModule.removeGetRegistrationIdListener(getRegistrationIdEvent);
JPushModule.clearAllNotifications();
} else {
DeviceEventEmitter.removeAllListeners();
NativeAppEventEmitter.removeAllListeners();
}
}
backHandle = () => {
const currentScreen = getCurrentScreen(this.props.router);
//登录
if (currentScreen === 'Login') {
return true;
}
if (currentScreen !== 'Home') {
this.props.dispatch(NavigationActions.back());
return true;
}
return false;
}
render() {
if (!this.state.configload) {
return (
<View style={{ flex: 1 }}>
<StatusBar
barStyle="light-content"
/>
{/* <ScanNetConfig ScanSuccess={() => {
this.setState({ configload: true });
}}
/> */}
<Text>ScanNetConfig</Text>
</View>
);
}
const { dispatch, router } = this.props;
const navigation = addNavigationHelpers({ dispatch, state: router });
return (
<View style={{ flex: 1 }}>
<AppNavigator navigation={navigation} />
</View>
);
}
}
export function routerReducer(state, action = {}) {
return AppNavigator.router.getStateForAction(action, state);
}
export default Router;
Need More Detail?
this is the error.
bundling failed: SyntaxError in D:\RN\AtmosphericMeshing-\src\router.js: D:/RN/AtmosphericMeshing-/src/router.js: Unexpected token (16:0)
I find the Solution
.babelrc needs to be changed:
{
"presets": ["react-native"],
"plugins": [
"syntax-decorators",
"transform-decorators-legacy",
["import", { "libraryName": "antd-mobile" }]
]
}