I am facing one issue in my drawer. I am looking for some help in this. People who are using react navigation 5 version.
i am using the latest react navigation version and my code for custom drawer is below
function CustomDrawerContent(props) {
//
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
<DrawerItem label="Logout" onPress={()=>
Alert.alert(
'Log out',
'Do you want to logout?',
[
{text: 'Cancel', onPress: () => {return null}},
{text: 'Confirm', onPress: () => {
AsyncStorage.clear();
//props.navigation.navigate('Home')
}},
],
{ cancelable: false }
)
} />
</DrawerContentScrollView>
);
}
And my route.js is mentioned below.
export default function Routes(username, password) {
// eslint-disable-next-line no-shadow
const Stack = createStackNavigator();
const [loader, setloader] = useState('');
const [state, dispatch] = React.useReducer(
(prevState, action) => {
switch (action.type) {
case 'RESTORE_TOKEN':
return {
...prevState,
userToken: action.token,
isLoading: false,
};
case 'SIGN_IN':
return {
...prevState,
isSignout: false,
userToken: action.token,
};
case 'SIGN_OUT':
return {
...prevState,
isSignout: true,
userToken: undefined,
};
}
},
{
isLoading: true,
isSignout: false,
userToken: null,
},
);
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
const bootstrapAsync = async () => {
let userToken;
try {
userToken = await AsyncStorage.getItem('#kiklee-user-id');
} catch (e) {
// Restoring token failed
}
// After restoring token, we may need to validate it in production apps
// This will switch to the App screen or Auth screen and this loading
// screen will be unmounted and thrown away.
dispatch({type: 'RESTORE_TOKEN', token: userToken});
};
bootstrapAsync();
}, []);
const authContext = React.useMemo(
() => ({
signIn: async data => {
try {
let config_api_url = Config.api_login_url;
if (data.username === '' && data.password === '') {
Alert.alert(
'Error : ',
'Please enter your email address and password.',
);
} else if (data.username === '') {
Alert.alert('Error : ', 'Please enter your email address.');
} else if (
!(data.username === '') &&
Validate.isEmailValid(data.username) === true
) {
Alert.alert('Error : ', 'Please enter the correct email address.');
} else if (password.length < 5) {
Alert.alert(
'Error : ',
'Please enter your password with a minimum length of 5.',
);
} else {
// seterror(''); //empty all errors
setloader(true);
await fetch(config_api_url, {
method: 'POST',
body: JSON.stringify({
username: data.username,
password: data.password,
}),
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
.then(response => response.text())
.then(async responseData => {
// parse the json values
var responsevalue = JSON.parse(responseData);
if (responsevalue.login_status === 'Active') {
await AsyncStorage.removeItem('#kiklee-user-id');
await AsyncStorage.setItem(
'#kiklee-user-id',
responsevalue.id.toString(),
);
// const value = await AsyncStorage.getItem('#kiklee-user-id');
dispatch({type: 'SIGN_IN', token: 'dummy-auth-token'});
setloader(false);
// eslint-disable-next-line eqeqeq
} else if (responsevalue.login_status == 'Deactive') {
Alert.alert('Error : ', 'Userid is deactive.');
setloader(false);
} else {
Alert.alert('Error : ', 'Invalid username or password.');
setloader(false);
}
})
.catch(err => {
Alert.alert(err);
});
}
} catch (e) {
// saving error
Alert.alert('Please try your login after some time.');
}
},
signOut: async () => {
AsyncStorage.removeItem('#kiklee-user-id');
dispatch({type: 'SIGN_OUT'});
},
signUp: async data => {
dispatch({type: 'SIGN_IN', token: 'dummy-auth-token'});
},
}),
[],
);
if (state.isLoading) {
// We haven't finished checking for the token yet
return <SplashScreen />;
}
// Loader
if (loader == true) {
return (
<View style={styles.container}>
<Spinner
visible={true}
textContent={'Loading...'}
textStyle={styles.spinnerTextStyle}
/>
</View>
);
}
return (
<AuthContext.Provider value={authContext}>
<NavigationContainer>
{state.userToken == null ? (
<>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerShown: false,
animationTypeForReplace: state.isSignout ? 'pop' : 'push',
}}
/>
<Stack.Screen
name="Privacy"
component={Privacy}
options={{headerShown: false}}
/>
<Stack.Screen
name="ForgetPassword"
component={ForgetPassword}
options={{headerShown: true}}
/>
<Stack.Screen
name="SignUp"
component={Signup}
options={{headerShown: false}}
/>
</Stack.Navigator>
</>
) : (
<>
<Drawer.Navigator
initialRouteName="Dashboard"
drawerContent={props => CustomDrawerContent(props)}>
<Drawer.Screen name="Dashboard" component={MainRoot} />
</Drawer.Navigator>
</>
)}
</NavigationContainer>
</AuthContext.Provider>
);
}
i can call authcontext inside the custom drawer because it will display a error called "Hooks rule"
i am looking for a help only with react navigation 5.
Try defining const Stack = createStackNavigator(); above the Routes component.
Your useState and useReducer hooks need to come first.
I had the same issue ,but eventually I created a work around because I tried passing the function as props from different roots and when i logged props from the customer drawer item , it was undefined... so that lead me to a previous solution that I had used with react navigation v4.
Solution:
create your own component for signing out and other functionalities like links etc.
const DrawerMeta = () => {
const { signOut } = useContext(AuthContext);
return (
<View style={{ flex: 1, justifyContent: "flex-end" }}>
<Button
onPress={async () => {
await signOut();
}}
title="sign out"
/>
</View>
);
};
insert into your drawer component
function CustomDrawerContent(props) {
//
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
<DrawerMeta />
</DrawerContentScrollView>
);
}
Related
I have problem jump to the specific tab navigation base on my dynamic initial route name.
Here is the scenario:
If the user just want to login, The initial route of Auth tab navigation must be on ProductBrowsing
If the user first time in the application and user register in the app, The initial route of Auth tab navigation must be on AddProduct.
Problem: When I login the screen not changing the screen stuck on login the navigation replace is not working.
Handler Login:
const HandlerLoginBtn = async () => {
const user_data = {
mobile_number: username,
password: password
}
await dispatch(LoginAuthentication(user_data)).then((res) => {
if(res?.payload?.message == 'Login Success') {
if(redirection == 'ProductBrowsing') {
navigation.replace('ProductBrowsing')
}
else if(redirection == 'AddProduct') {
navigation.replace('AddProduct')
}
}
})
}
Auth Tab:
function AuthenticateTabNav() {
return (
<Tab.Navigator tabBarOptions={{
inactiveTintColor: '#032363',
labelStyle: { fontSize:12, fontWeight:'500'},
style: {
backgroundColor: '#ffff',
}
}}
>
<Tab.Screen
options={{
tabBarLabel: 'BUY',
tabBarIcon: ({ focused , color, size }) => (
focused ? (
<Image h="4" w="4"
source={require('./assets/tab_icons/buy/blue_buy.png')}></Image>
): (
<Image h="4" w="4"
source={require('./assets/tab_icons/buy/black_buy.png')}></Image>
)
),
}}
name="ProductBrowsing"
component={ProductBrowsingScreen}
/>
<Tab.Screen
options={{
tabBarLabel: 'SELL',
tabBarIcon: ({ focused , color, size }) => (
focused ? (
<Image h="4" w="4"
source={require('./assets/tab_icons/sell/blue_sell.png')}></Image>
): (
<Image h="4" w="4"
source={require('./assets/tab_icons/sell/black_sell.png')}></Image>
)
),
}}
name="AddProduct"
component={AddProductScreen}
/>
</Tab.Navigator>
)
}
Navigation:
const AuthenticatedNav = () => {
const {redirection} = useSelector((state) => state.user)
const [firstScreen, setFirstScreen] = useState('');
console.log(firstScreen)
useEffect(() => {
setFirstScreen(redirection)
}, [])
return (
<Stack.Navigator initialRouteName={firstScreen == '' ? 'ProductBrowsing' : 'AddProduct'} headerMode='none'>
<Stack.Screen component={AuthenticateTabNav} name={firstScreen == '' ? 'ProductBrowsing' : 'AddProduct' } />
</Stack.Navigator>
)
}
Redux State:
builder.addCase(LoginAuthentication.fulfilled, (state, action) => {
if(action.payload?.message == 'Invalid Username Or Password') {
state.auth_access = []
state.isLoggedIn = false
state.isLoading = false
state.redirection = ''
}else {
state.auth_access = action.payload
state.isLoggedIn = true
state.redirection = 'ProductBrowsing'
}
})
I've made a full sample here https://snack.expo.dev/O3vW24UCl
If I understand correctly, you want to navigate from one tab to another depending on the user login state.
Changing the initialRouteName on the main Stack is not useful as you are moving within the tabs in your case.
What you want to do is the same but on the Tab.Navigator.
const Tabs = () => {
const isUserLoggedIn = useSelector((state) => {
return state.isUserLoggedIn;
});
return (
<Tab.Navigator
initialRouteName={isUserLoggedIn ? "AddProduct" : "ProductBrowsing"}
>
<Tab.Screen
options={{
tabBarLabel: 'BUY',
}}
name="ProductBrowsing"
component={ProductBrowsingScreen}
/>
<Tab.Screen
options={{
tabBarLabel: 'SELL',
}}
name="AddProduct"
component={AddProductScreen}
/>
</Tab.Navigator>
);
};
Obviously, in the sample the state is not persisted.
I have MySheets where I need to show some info. This info is fetched from a graphql endpoint. The problem is that the component is loading some time and then the following error occurs:
Relay request for MySheetsQuery failed by the following reasons: 1.
System cannot complete request. Something went wrong trying to
retrieve My Asset Allocations for {(R1) RSA 11% 2012} from external
sources sheets(first: $count, after: $cursor, q: $search, id^^^.
The status code is 200 OK. ,
but the response I am getting in Network is:
{"errors":[{"message":"System cannot complete request. Something went
wrong trying to retrieve My Asset Allocations for {(R1) RSA 11% 2012}
from external
sources","locations":[{"line":25,"column":3}],"path":["viewer","sheets"],"extensions":{"type":"BUSINESS_CONSTRAINT_VIOLATION","classification":"DataFetchingException"}}],"data":null}
When I copy the request payload from the Inspect -> Network and the values I pass and paste them in Postman, I get the response successfully. Why is relay causing an error?
I am getting this warnings from relay in console.
const MySheets: React.FC<IProps> = ({
viewer,
id,
name,
}: IProps) => {
const [localSearch, setLocalSearch] = React.useState<string>();
const [search, setSearch] = React.useState<string>();
return (
<>
<div>
<HeaderActionBar title={"Sheets"} />
<Paper elevation={6}>
<Box display={"flex"} margin={2.5}>
<div>
<div>
<SearchIcon fontSize={"medium"} color={"action"} />
</div>
<InputBase
onChange={(event) => {
setLocalSearch(event.target.value);
}}
placeholder={"Search..."}
onKeyDown={(e) => {
if (e.keyCode === 13) {
setSearch(localSearch);
}
}}
inputProps={{ "aria-label": "search" }}
/>
</div>
<Button
variant={"contained"}
size={"small"}
color={"secondary"}
startIcon={
<SearchIcon fontSize={"large"} color={"inherit"} />
}
onClick={() => {
setSearch(localSearch);
}}
>
Search
</Button>
</Box>
<Divider/>
<Suspense fallback={<TableSkelton />}>
<SheetsTable
viewer={viewer}
search={search}
id={id}
/>
</Suspense>
</Paper>
</div>
</>
);
};
const query = graphql`
query MySheetsQuery(
$count: Int!
$cursor: String
$search: String
$clientId: ID!
$supplierIds: [ID!]
$supplierProductIds: [ID!]
$instrumentName: String
$sort: SheetSort
) {
viewer {
...SheetsTable_sheets
}
node(id: $clientId) {
... on Client {
id
displayName
}
}
}
`;
export function SheetsWrapper() {
const { id } = useParams();
const { data, isLoading, error } = useQuery<MySheetsQuery>(query, {
count: 10,
search: null,
id: id ?? null,
});
if (isLoading) {
return <EnhancedCircularProgress />;
}
if (error) {
return <><div>{error.message}</div>
<div>{error.stack}</div></>;
}
if (!data) {
return <EnhancedCircularProgress />;
}
return (
<Suspense fallback={<EnhancedCircularProgress />}>
<MySheets
viewer={data?.viewer}
clientId={id}
clientName={data.node?.displayName ?? ""}
/>
</Suspense>
);
}
interface IProps {
viewer: SheetsTable_sheets$key;
clientId: string;
clientName: string;
}
export default SheetsWrapper;
This is my RelayEnvironment:
type HandleLogoutFn = () => void;
type GetAuthTokenFn = () => string;
function createNetworkLayer(
handleLogout: HandleLogoutFn,
getAuthTokenFn: GetAuthTokenFn
) {
return new RelayNetworkLayer(
[
cacheMiddleware({
size: 100, // max 100 requests
ttl: 900000, // 15 minutes
}),
urlMiddleware({
url: () => `${ConfigService.serverUri}/graphql`,
}),
retryMiddleware({
fetchTimeout: 5000,
retryDelays: [30000, 60000],
forceRetry: (cb, delay) => {
window.forceRelayRetry = cb;
console.log(
`call \`forceRelayRetry()\` for immediately retry! Or wait ${delay} ms.`
);
},
statusCodes: [500, 503, 504],
}),
authMiddleware({
token: getAuthTokenFn,
}),
(next) => async (req) => {
req.fetchOpts.headers.Accept = "application/json";
req.fetchOpts.headers["Content-Type"] = "application/json";
// req.fetchOpts.headers['X-Request-ID'] = uuid.v4(); // add `X-Request-ID` to request headers
req.fetchOpts.credentials = "same-origin"; // allow to send cookies (sending credentials to same domains)
try {
return await next(req);
} catch (ex) {
if (ex.res && ex.res.status === 401) {
handleLogout();
}
throw ex;
}
},
],
{}
);
}
export function createRelayEnv(
handleLogout: HandleLogoutFn,
getAuthTokenFn: GetAuthTokenFn
) {
const network = createNetworkLayer(handleLogout, getAuthTokenFn);
return new Environment({
network: network,
store: new Store(new RecordSource()),
});
}
I'm creating a custom Formio app using React and I'm trying to create a component that we can use to open previously created forms for editing. I use the component from Formio, and then try to pull the components and pass them to the components attribute, but it keeps telling me it's undefined. However, if I stringify it, and even import it using a variable or paste the exact string in, it works, but if I try to pass it to a variable or state, it won't work. Below is my code and any help is much appreciated. I feel like I've tried everything.
import { FormEdit, FormBuilder } from "react-formio";
import axios from "axios";
import AppLayout from "../pages/layout/appLayout";
import { Typography } from "#material-ui/core/";
import { Link } from "react-router-dom";
import { components } from "../formData";
export default class FormUpdate extends React.Component {
state = {
formName: "",
formTitle: "",
isFormSubmitted: false,
formComponents: {},
errorMsg: "",
};
componentDidMount() {
console.log("Id", this.props.formData);
axios
.get(
`https://awsid.execute-api.us-east-1.amazonaws.com/prod/form?formName=${this.props.formData.id}`,
{ crossDomain: true },
{
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET,POST,OPTIONS",
},
}
)
.then((res) => {
console.log(res.data.form.components);
this.setState({
formComponents: res.data.form.components[0],
});
})
.catch((error) => {
console.log(error);
this.setState({ errorMsg: "Error retrieving form data." });
});
}
updateForm = (form) => {
const payload = {
form,
};
console.log("Form Data:", form);
/* Code to fix JSON of select boxes component. If no default value is selected, default value attribute is deleted */
for (var key in payload.form.components) {
if (
form.components[key].defaultValue === "" ||
form.components[key].defaultValue === null ||
// form.components[key].defaultValue === false ||
form.components[key].defaultValue.hasOwnProperty("")
) {
console.log(
form.components[key].defaultValue + " is blank. Deleting attribute."
);
delete form.components[key].defaultValue;
}
console.log(form.components[key].defaultValue);
}
return axios
.put(
`https://awsid.execute-api.us-east-1.amazonaws.com/prod/form`,
payload,
{ crossdomain: true },
{
headers: {
"Content-type": "application/json",
},
}
)
.then((response) => {
console.log("Uploaded", response.data);
this.setState({
formName: response.data.id,
formTitle: response.data.title,
isFormSubmitted: true,
});
})
.catch((error) => {
console.log("Did not post: " + error);
});
};
handleFormNameChange = (evt) => {
const value = evt.target.value;
this.setState({
...this.state,
formName: value,
});
};
handleFormNameClick = async () => {
await this.setState({
formNameForm: false,
});
this.retrieveData(this.state.formName);
// console.log(this.state.formName);
};
listComponents = () => {
this.state.formComponents.map((item) => {
console.log(item);
// this.setState({ formComponents: item });
return item;
});
};
components = JSON.stringify(this.state.formComponents).replace(
/^\[(.+)\]$/,
"$1"
);
render() {
// const components = JSON.stringify(this.state.formComponents).replace(
// /^\[(.+)\]$/,
// "$1"
// );
// const allComponents = this.state.formComponents[0];
console.log(
JSON.stringify(this.state.formComponents).replace(/^\[(.+)\]$/, "$1")
);
console.log(this.state.formComponents);
console.log(components);
return (
<AppLayout>
<div className="container">
<link rel="stylesheet" href="styles.css" />
<link
rel="stylesheet"
href="https://unpkg.com/formiojs#latest/dist/formio.full.min.css"
/>
<link
href="https://fonts.googleapis.com/css2?family=Bangers&display=swap"
rel="stylesheet"
></link>
<main className="formEditContainer">
{this.state.isFormSubmitted === true ? (
<div className="fadePanel">
<Typography variant="h4" align="center">
Form Completed
</Typography>
<br />
<Typography variant="body1" align="center">
<b>
<i> To view form visit:</i>
</b>{" "}
<Link
to={`/openform/${this.state.formName}`}
className="links"
>
<small>{this.state.formTitle}</small>
</Link>
</Typography>
</div>
) : (
<div>
<Typography>
Form Name:
{this.props.formData.id}
</Typography>
<br />
<FormEdit
form={{
display: "form",
title: this.props.formData.id,
name: this.props.formData.id,
path: this.props.formData.id,
components: [components],
}}
saveText={"Save Form"}
saveForm={(form) => this.updateForm(form)}
>
<FormBuilder
form={{
display: "form",
title: this.props.formData.id,
}}
Builder={{}}
onUpdateComponent={{}}
/>
</FormEdit>
</div>
)}
<br />
<br />
</main>
</div>
</AppLayout>
);
}
}
I am trying to handle the HTTP error in my application for the unknown path using catch and by changing the state of the application. I am changing the state of the application to display the details of the unknown path but it's not working. Can anyone suggest me how to do that? I have added my code below
App.js
import React, { Component } from 'react'
import charactersFile from "./data/characters.json"
import axios from 'axios';
import './App.css';
class App extends Component {
state = {
movies: [],
loading: true,
error: ''
};
handleClick = character => {
console.log(character.name);
const PeopleUrl = `https://swapi.co/api/people/`;
const FilmUrl = `https://swapi.co/api/films/`;
switch (character.name) {
case "Luke Skywalker":
axios
.get(`${PeopleUrl}1/`)
.then(response =>
Promise.all([
axios.get(`${FilmUrl}2/`),
axios.get(`${FilmUrl}6/`),
axios.get(`${FilmUrl}3/`),
axios.get(`${FilmUrl}1/`),
axios.get(`${FilmUrl}7/`)
])
)
.then(result =>
result.map(values =>
this.setState({
movies: [
...this.state.movies,
{
title: values.data.title,
release_date: values.data.release_date
}
],
loading: false,
render: true
})
)
);
break;
case "C-3PO":
axios
.get(`${PeopleUrl}2/`)
.then(response =>
Promise.all([
axios.get(`${FilmUrl}2/`),
axios.get(`${FilmUrl}5/`),
axios.get(`${FilmUrl}4/`),
axios.get(`${FilmUrl}6/`),
axios.get(`${FilmUrl}3/`),
axios.get(`${FilmUrl}1/`)
])
)
.then(result =>
result.map(values =>
this.setState({
movies: [
...this.state.movies,
{
title: values.data.title,
release_date: values.data.release_date
}
],
loading: false,
render: true
})
)
);
break;
case "Leia Organa":
axios.get(`${PeopleUrl}unknown/`)
.then(response => {
if (response.status === 404) {
this.setState({ error: "Details not found" })
}
}).catch(error => {
console.log(error); // They are other network errors
this.setState({ error: 'Network error' })
})
break;
case "R2-D2":
axios
.get(`${PeopleUrl}3/`)
.then(response =>
Promise.all([
axios.get(`${FilmUrl}2/`),
axios.get(`${FilmUrl}5/`),
axios.get(`${FilmUrl}4/`),
axios.get(`${FilmUrl}6/`),
axios.get(`${FilmUrl}3/`),
axios.get(`${FilmUrl}1/`),
axios.get(`${FilmUrl}7/`)
])
)
.then(result =>
result.map(values =>
this.setState({
movies: [
...this.state.movies,
{
title: values.data.title,
release_date: values.data.release_date
}
],
loading: false,
render: true
})
)
);
break;
default:
return "No list item";
}
};
render() {
console.log(this.state);
const Content = this.state.loading ? (
<div style={{ marginTop: "20px", padding: "20px" }}>"Loading..."</div>
) : (
<ul>
{this.state.movies.map(movie => (
<li key={movie.title}>
{movie.title} - {movie.release_date}
</li>
))}
</ul>
);
const List = (
<ul>
{charactersFile.characters.map(character => {
return (
<li
key={character.name}
onClick={() => this.handleClick(character)}
>
{character.name}
</li>
);
})}
</ul>
);
return <div className="App">{!this.state.render ? List : Content}</div>;
}
}
export default App;
characters.json
{
"characters": [
{
"name": "Luke Skywalker",
"url": "https://swapi.co/api/people/1/"
},
{
"name": "C-3PO",
"url": "https://swapi.co/api/people/2/"
},
{
"name": "Leia Organa",
"url": "https://swapi.co/api/people/unknown/"
},
{
"name": "R2-D2",
"url": "https://swapi.co/api/people/3/"
}
]
}
The problem is
You don't set loading to false when error occurs.
You don't check for errors in your render method.
First lets extract the presentation components to small function components to make the main component more simple
const Error = ({ message }) => <h3>{message}</h3>;
const Loader = () => (
<div style={{ marginTop: "20px", padding: "20px" }}>"Loading..."</div>
);
const List = ({ handleClick }) => (
<ul>
{charactersFile.characters.map(character => {
return (
<li key={character.name} onClick={() => handleClick(character)}>
{character.name}
</li>
);
})}
</ul>
);
const Content = ({ movies }) => (
<ul>
{movies.map(movie => (
<li key={movie.title}>
{movie.title} - {movie.release_date}
</li>
))}
</ul>
);
App
class App extends Component {
state = {
movies: [],
loading: false,
error: ""
};
handleClick = character => {
// Set loading to true and error to false
this.setState({ loading: true, error: false, movies: [] });
console.log(character.name);
axios
.get(character.url)
.then(({ data }) =>
Promise.all(data.films.map(filmUrl => axios.get(filmUrl)))
)
.then(result => {
const movies = result.map(({ data: { title, release_date } }) => ({
title,
release_date
}));
this.setState({ movies, loading: false, error: "" });
})
.catch(() => {
this.setState({
movies: [],
loading: false,
error: "List not found"
});
});
};
render() {
const { error, loading, movies } = this.state;
return (
<div className="App">
{/* include Content component only when there is data */}
{movies.length > 0 ? (
<Content movies={movies} />
) : (
<List handleClick={this.handleClick} />
)}
{/* include Loader component when loading */}
{loading && <Loader />}
{/* include Error component when there is an error */}
{error && <Error message={error} />}
</div>
);
}
}
If you have any questions about the code let me know.
Working sandbox
404 error was judged not by catch but by normal request.
So you can catch 404 like .then(res => {if(res.status === 404)} or .then(res => { if(!res.ok) })
Some example for your code
axios.get(`${PeopleUrl}unknown/`)
.then(response => {
if(response.ok) {
console.log(response.data);
} else {
console.log('Details not found.'); // It is 404 error
this.setState({ error : 'Details not found' });
}
})
.catch(error => {
console.log(error); // They are other network errors
this.setState({ error : 'Network error' })
})
I have 3 records in my table, I can see the app fetches record to my remote because I console.log the response. My problem is that it will not display the item.
I know I defined correctly the column in FlatList because If I will set the per_page=1 which means pull 1 record every request. It will display but 2 records only will display the last record will not, if I set to per_page=30 nothing displays. is there a problem in my setState() during the response ?.I heard that setSate is not mutable..how can I apply the updater function of setsate in my code.?...I am still fresh on react native I hope someone will help me here.
I tried to do this but no luck!..also is this will matter that I use react-redux in my other page then in this screen I did not use only handling of state. ?...please help me react-native experts.
this.setState({
page: this.getParameterByName('page', res.next_page_url),
data: this.state.page === 1 ? res.data : [...this.state.data, ...res.data],
error: res.error || null,
loading: false,
refreshing: false,
last_page: res.last_page
},()=>{
return this.state;
});
Here is my complete code
import React, { Component } from 'react';
import {ScrollView, Text, View, Button, FlatList, ActivityIndicator} from 'react-native';
import { List, ListItem, Icon } from "react-native-elements";
import {connect} from "react-redux";
import numeral from "numeral";
import Moment from 'react-moment';
import moment from 'moment';
class Screen1 extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
per_page: 30,
order_by:'id',
sort_by:'asc',
error: null,
refreshing: false,
param:'',
last_page:''
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const {page, per_page,order_by,sort_by } = this.state;
const url = `http://myapp.com/api/mobile/credit?page=${page}&api_token=${this.props.token}&per_page=${per_page}&order_by=${order_by}&sort_by=${sort_by}`;
console.log("the url",url);
this.setState({ loading: true });
setTimeout(()=>{
fetch(url)
.then(res => res.json())
.then(res => {
console.log("the page is =",this.getParameterByName('page',res.next_page_url));
this.setState({
page:this.getParameterByName('page',res.next_page_url),
data: this.state.page === 1 ? res.data : [...this.state.data,...res.data],
error: res.error || null,
loading: false,
refreshing: false,
last_page: res.last_page
});
})
.catch(error => {
this.setState({ error, loading: false });
});
},1500);
};
handleRefresh = () => {
if( this.state.page) {
if (this.state.page <= this.state.last_page) {
this.setState(
{
refreshing: true,
page: this.state.page
},
() => {
this.makeRemoteRequest();
}
);
}
}
};
getParameterByName = (name,url) =>{
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
let regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return parseInt(decodeURIComponent(results[2].replace(/\+/g, " ")), 10);
};
handleLoadMore = () => {
if( this.state.page){
if( this.state.page <= this.state.last_page ){
this.setState(
{
page: this.state.page
},
() => {
this.makeRemoteRequest();
}
);
}else{
console.log("cannot handle more",this.state.page)
}
}else{
console.log("page is null");
}
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
);
};
renderHeader = () => {
return (
<View >
<Text h1
style={{
color: 'blue',
fontWeight: 'bold',
textAlign: 'center',
fontSize: 30,
backgroundColor: "#CED0CE",
}}
>{ numeral(this.props.thetotalcredit).format("#,##0.00") }</Text>
</View>
);
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
return (
<FlatList
data={this.state.data}
keyExtractor = {(item, index) => index.toString()}
renderItem={({ item }) => (
<ListItem
title= { numeral(item.amountcredit).format("#,##0.00") }
subtitle= { moment(item.creditdate).format("MMM DD, YYYY") }
containerStyle={{ borderBottomWidth: 0 }}
/>
)}
extraData={this.state.data}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.5}
stickyHeaderIndices={[0]}
/>
);
}
}
const mapStateToProps = (state) => {
return {
username: state.auth.username,
token:state.auth.token,
thetotalcredit:state.auth.total_credit
};
};
export default connect(mapStateToProps)(Screen1);