So, I am new to react Native and I am taking over code that was written like 5 years ago for a react Native App. Since I know things moved a lot since then I want to make sure I follow best practices.
So basically, API responses are saved in a state.
Then for each screen, there is a StackNavigator using createStackNavigator
The render method in those stack navigator look like this
import { InfoContainer } from "../api/InfoContainer ";
....
....
....
render() {
return (
<Subscribe
to={[ InfoContainer ]}
>
{(InfoStore) => (
<SettingsStack
navigation={this.props.navigation}
screenProps={{
store: InfoStore,
}}
/>
)}
</Subscribe>
);
}
}
Then there is a tabNavigator.js that aggregates all the StackNavigators for each screen
and it looks like this
export default createBottomTabNavigator(
{
Overview: {
screen: HomeStack,
navigationOptions: {
title: "Home",
},
then on that screen, the API data is accessed like this
await InfoStore.fetchInfo(currentUser);
if someone could advise of what's the best way to do this and refer to some documentation or article as I could not find anything relevent.
Related
I'm an iOS developer and I've been struggling for what seems like the longest time making my portfolio site from scratch. I've tried a bunch of different technologies and have finally settled on using Gatsby to create it.
So far things have been fairly straightforward but I can not figure out for the life of me how to get a component that looks like the picture below. I've gotten most of the layout design working, but I can't seem to use graphql to query the images I need in the component.
Desired Layout
I've found plenty of Gatsby example templates such as this one and this one that are similar. However the main difference is that each of these only have one image and they seem to be using Gatsby 2.0 instead of 3.0.
I can get one image using "useStaticQuery", however I need access to different images for each component. From my understanding this is not possible to do within a component, only on a page. I also can not pass the image path as a variable to StaticImage either.
export default function App(props) {
const query = useStaticQuery(graphql`
query AppSectionImages {
icon: file(relativePath: { eq: "EzMaxRequest/AppIcon_180.png" }) {
childImageSharp {
gatsbyImageData(
width: 200
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
`);
const image = getImage(query.icon);
const app = props.app;
return (
<div>
<h1>{app.title}</h1>
<GatsbyImage image={image} />
</div>
);
Result
Can anyone please explain to me how I can get the desired layout in a component?
Edit
Here is some relevant code of what I am doing.
This is my index.js home page.
export default function IndexPage({ data }) {
const projects = data.apps.edges;
return (
<Layout>
<SEO title="Home" />
<HeroSection />
<DescriptionSection />
<div>
{projects.map(({ node: project }) => (
<AppSection app={project} />
))}
</div>
<FooterSection />
</Layout>
);
}
//export page query
export const query = graphql`
query Apps {
apps: allAppsJson(sort: { order: ASC, fields: order }) {
edges {
node {
appLink
title
tagline
moreLink
order
icon
}
}
}
}
`;
Here is the component.
export default function App(props) {
const query = useStaticQuery(graphql`
query AppSectionImages {
icon: file(relativePath: { eq: "EzMaxRequest/AppIcon_180.png" }) {
childImageSharp {
gatsbyImageData(
width: 200
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
`);
const image = getImage(query.icon);
const app = props.app;
return (
<div>
<h1>{app.title}</h1>
<GatsbyImage image={image} alt={app.title} />
</div>
);
}
You have a few options:
Query for all of your image data in your page query and prop-drill the data to the component that uses it to display the image.
Using Gatsby v3+, hardcode the image references for each component using the new StaticImage component.
If you have a single component used multiple times with different content/images, but a static parent component with your content, you can leverage option #2 above but pass the image component down as a prop or children.
I'm using PDFDownloadLink from the react-pdf package to generate a PDF on the fly in my application and allow the user to download a report based on data being passed to the component that generates the PDF document. However, there are more than 400 pages that need to be rendered in this PDF, and this operation blocks the main thread for a few seconds. Is there any way to make this operation asynchronous, so the rest of the application will continue to function while the PDF is being generated? Also I would like to be able to cache the results, since the data being passed to the component can come from about 8 different arrays of data, which don't change very much, so switching between these arrays I would rather not to have to render the PDF all over again if the PDF for that given array has already been generated once before... I'm guessing the blob data needs to be stored somewhere, perhaps localStorage?
import { Page, Text, View, Document, StyleSheet, PDFDownloadLink } from '#react-pdf/renderer'
const App = () => {
const condition = "firstCondition";
const filteredRowData = rowData.filter(a => a.condition = condition);
return (
<PDFDownloadLink
document={<PDF_REPORT_Document rowData={filteredRowData} />}
fileName={"PDF_REPORT.pdf"}
style={{color:'white'}}
>{({ blob, url, loading, error }) =>
loading ? "Report loading..." : "Report ready to download"
}</PDFDownloadLink>
);
}
const PDF_REPORT_Document = (props) => {
const { rowData } = props;
const styles = StyleSheet.create({
page: {
flexDirection: 'column',
backgroundColor: '#E4E4E4'
},
section: {
margin: 10,
padding: 10,
flexGrow: 1
}
});
return(
<Document>
{rowData.map((row,index) =>
<Page size="A4" style={styles.page} key={index}>
<View style={styles.section}>
<Text>Name: {row.FULLNAME}</Text>
</View>
</Page>
)}
</Document>
);
}
I finally found the answer to this in an issue on github which addresses this exact problem:
Is your feature request related to a problem? Please describe.
It is an improvement. At the moment, if you use 'PDFDownloadLink' the PDF is being generated as the component loads.
Describe the solution you'd like
It is not mandatory, but having multiple heavy PDFs ready to be downloaded wouldn't be the best approach since not every user will need it.
Describe alternatives you've considered
I've used pdf() function to generate the blob and file-saver lib to download it:
import { saveAs } from 'file-saver';
import { pdf } from '#react-pdf/renderer';
import PdfDocument from '../PdfDocument';
const generatePdfDocument = async (documentData,fileName) => {
const blob = await pdf((
<PdfDocument
title='My PDF'
pdfDocumentData={documentData}
/>
)).toBlob();
saveAs(blob, fileName);
};
export default generatePdfDocument;
I'm using a react-navigation. More specifically, I have a materialTabNavigator nested inside of a drawerNavigator. Each tab is in itself a stackNavigator. I have a button in homeScreen, that navigates to makePost.js. There I take in information and store it to Async storage using a simple wrapper.
In Posts.js there's a FlatList displaying each post as a component. The data for the FlatList is initially set correctly after making a request from Async Storage. The problem is that this only happens when the app is first opened. I have tried many different approaches to solve this. The only way so far I've found is to continuously setState in ComponentDidUpdate() in Posts.js. Obviously this is problematic, because it re-renders constantly. I can set a flag to stop is from rendering, but then it will not re-render again.
Ultimately, what I'd like to happen is that when I hit the user is done entering their information and is ready to make a post, they hit the button in makePost.js, and the data in the FlatList of Posts.js is update.
I've tried to pass parameters using navigation, does not work, parameters get lost somewhere, probably because of the nested navigators.
I could really used some guidance on the proper way to accomplish this.
( Navigators; not sure why this is forcing to one line )
---drawer
--tabNav
-home
homeScreen.js
makePost.js
-posts
posts.js
-messages
--drawer1
--drawer2
//Posts.js
export default class Posts extends React.Component {
state = {
rows: [
{id: 0, text: "dog"},
],
}
componentDidMount() {
this.loadState();
}
loadState = () => {
var value = store.get('posts').then((res => {
if (res === null) {
res = [{id: 0, text: "default"}]
} else {
res = res
}
this.setState({rows: res})
}))
}
componentDidUpdate() {
this.loadState();
}
renderItem = ({item}) => {
return (
<BoardTab style={styles.row} />
)}
render() {
return (
<View style={styles.view}>
<FlatList
ListFooterComponent={this.renderFooter}
style={styles.container}
data={this.state.rows}
renderItem={this.renderItem}
keyExtractor={extractKey}
>
</FlatList>
<BoardScreenFooter />
</View>
);
}
And Posts.js button looks like this:
<TouchableOpacity
onPress={ () => {
this._onPressButton
this.storeFunc(this.state.newPost)
const retval = this.state.rows
this.props.navigation.navigate('Board',
{rowsID: retval});
}
}>
<Icon
reverse
name='md-camera'
type='ionicon'
color='green'
size={12}
/>
</TouchableOpacity>
storeFunc(newObj) {
newObj.id = newObj.id + 1
store.push('posts', newObj)
store.get('posts').then((res) => {
this.setState({rows: res})
})
}
Rapidly, i would say: use Redux. It alloq you to have global state in your app, which mean you can access the state anywhere (And also set them anywhere)
When opening the app, you get the data from the AsyncStore into the Redux store. You listen to the redux state (Which will be a props in your component) and display your list. When modifying your list in the other tab, you need to do 2 things:
Store the new data in the AsyncStorage
Update the state in the redux store. Since Posts.js will be listening at the redux store (as a props), it will re-render each time your data will change
A simple way to re-render a React-Navigation screen view on navigating to it:
All credit goes to Andrei Pfeiffer, Jul 2018, in his article: "Handle Tab changes in React Navigation v2" https://itnext.io/handle-tab-changes-in-react-navigation-v2-faeadc2f2ffe
I will reiterate it here in case the above link goes dead.
Simply add a NavigationEvents component to your render function with the desired listener prop:
render() {
return (
<View style={styles.view}>
<NavigationEvents
onWillFocus={payload => {
console.log("will focus", payload);
this.loadState();
}}
/>
<FlatList
ListFooterComponent={this.renderFooter}
style={styles.container}
data={this.state.rows}
renderItem={this.renderItem}
keyExtractor={extractKey}
>
</FlatList>
<PostScreenFooter />
</View>
);
}
Forked and following this tut:
https://github.com/alexandre-garrec/react-swipe-card/tree/master/src
But how did he get his actions? Also I want to refactor this for ES6 but I don't get what's in import { storiesOf, action } from '#kadira/storybook'; please help.
The tutorial looks simple as follows:
import Cards, { Card } from 'react-swipe-card'
const data = ['Alexandre', 'Thomas', 'Lucien']
const Wrapper = () => {
return (
<Cards onEnd={action('end')} className='master-root'>
{data.map(item =>
<Card
onSwipeLeft={action('swipe left')}
onSwipeRight={action('swipe right')}>
<h2>{item}</h2>
</Card>
)}
</Cards>
)
}
But some things are unanswered like: ={action('end')} - What are these actions? It appears they come from here: react-swipe-card/.storybook/config.js - but this file itself references: require('../stories/index.js'); and in index.js https://github.com/alexandre-garrec/react-swipe-card/blob/master/stories/index.js it again refrences: import { storiesOf, action } from '#kadira/storybook'; ...so slightly confusing. Can anyone make this work with ES6? I think I just need to know how to make action work or create a similar action with similar functionality.
I am using React Navigation inside React Native App and i created a Custom Header Component for my routes
like this :
const Router = StackNavigator({
Main: {
screen: Main,
navigationOptions: ({navigation}) => ({
header:<Header title="Main"/>
})
},
})
when using a custom header component the native animation not working
i would like to know how can i achieve the animation in the header the same as here https://reactnavigation.org/
TL:DR; found solution to share the animated.Value / interpolation over screens code below.
Animated Custom Header React-native + React navigation
This post was taunting me for some time - I was faced with the same issue. Hopefully this will reach you even if it's couple of months later :D
So first my issue was this, I made a component for custom header like in your example, my target was having one of the StackNavigator pages, have a scrollView which would in turn manipulate the color of the header.
Similar issue, the information exchange between header and page should help you too, here it goes.
Header.js
export class HeaderBar extends Component {
componentWillMount(){
// might be a bit, ehm but worked so if you have tips how to make the code nice feel free to share
let valueToManipulate= new Animated.Value(0);
this.props.navigation.setParams({
valueToManipulate,
customkey: valueToManipulate.interpolate({
inputRange: [0, 150],
outputRange: ['rgba(0,0,0,0)', 'rgba(0,0,0,1)'],
extrapolate: 'clamp',
})
})
}
render () {
... bit of code ...
// important bit for data binding !
if( ! (this.props.navigation.state.params && this.props.navigation.state.params.customkey) ){
return null;
}
/* unless that variable on params exists we do not ! render */
... bit more of code ...
<View style={ floating }>
<Animated.View style={{backgroundColor: this.props.navigation.state.params.customkey }}> /// <<--- typical bind
<View style={{flexDirection: 'row', justifyContent: "space-between"}}>
... and rest of render ...
So this is the header bit, now for the other "fun" part:
HomePage.js
export default class HomePage extends Component<{}> {
... stufff..... :D
render() {
/* this here, again data binding !, do not let render before we have this var in state params ! */
if( !( this.props.navigation.state.params && this.props.navigation.state.params.valueToManipulate ) )
return null;
return (
<ScrollView
style={styles.container}
onScroll={ Animated.event(
[{ nativeEvent: { contentOffset: { y: this.props.navigation.state.params.valueToManipulate } } }], // <-- tadaaa
)}
bounces={false}
scrollEventThrottle={1}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
>
... moar stuff ...
}
}
And here ! Finally ! A Demo !
Animated Custom Header React-native + React navigation
I published react-navigation-collapsible.
I hope it would be helpful.
https://github.com/benevbright/react-navigation-collapsible