How to open/close ExpandableCalendar of react-native-calendars programmatically? - react-native

I want to open/close ExpandableCalendar programmatically. I found there's only an initialPosition prop. It's not working when changing initialPosition. So I tried to use ref and call setPosition() function of ExpandableCalendar
export const MyScreen = () => {
const ref = useRef<ExpandableCalendar>()
return (
<CalendarProvider date={new Date()}>
<ExpandableCalendar ref={ref} />
</CalendarProvider>
)
}
when I use this code, the ref.current.contentRef is always undefined
My Question is
How to get the ref of ExpandableCalendar?
It seems the asCalendarConsumer saves the ref as contentRef:
function asCalendarConsumer(
WrappedComponent: React.ComponentType<any>
): React.ComponentClass {
class CalendarConsumer extends Component {
contentRef: any;
saveRef = (r: Ref<React.Component<any>>) => {
this.contentRef = r;
};
render() {
return (
<CalendarContext.Consumer>
{(context) => (
<WrappedComponent
ref={this.contentRef}
context={context}
{...this.props}
/>
)}
</CalendarContext.Consumer>
);
}
}
hoistNonReactStatic(CalendarConsumer, WrappedComponent);
return CalendarConsumer;
}
export default asCalendarConsumer;
/* ExpandableCalendar */
export default asCalendarConsumer(ExpandableCalendar);
Is there a way to open ExpandableCalendar programmatically? Does the setPosition() function work or there's another way to do this.
My Environment is
react-native-calendars#1.1266.0:
react-native#0.64.2:

Related

Iterate over values of Map to render all icon components but don't work however render one icon works

I am developing react-native project.
I have a function which set icon metadata into a Map :
export function getIconsMetadata() {
// a map of icons' metadata
const iconsMetadata = new Map();
...
// code to set icon metadata to the map
iconsMetadata.set("foo", "Foo");
iconsMetadata.set("bar", "Bar");
...
return iconsMetadata;
}
There is another function which returns the actual icon component based on the icon type (i.e. the value of iconsMetadata holds the icon type):
export function getMyIcon(iconType) {
switch (iconType) {
case 'Foo':
return <Foo />;
case 'Bar':
return <Bar />;
...
}
In my screen, I have a function to show icon component by iterating over the values of the above icons' metadata Map, and try to render each icon component:
export const MyScreen() => {
const showIcons = () => {
[...getIconsMetadata().values()].map((iconType, index) => {
const iconComponent = getMyIcon(iconType);
return <View key={index}>
{iconComponent}
</View>;
});
};
return (
<View style={styles.container}>
{/*I call the showIcons function here to render icons*/}
{showIcons()}
</View>
)
}
Problem is the icons are not shown on screen.
But if I directly return one icon component in my screen:
export const MyScreen = () => {
...
const showOneIcon = () => {
return <View>
<Foo />
</View>;
});
}
return (
<View style={styles.container}>
{/*I show one icon*/}
{showOneIcon()}
</View>
)
}
The <Foo /> icon component is rendered successfully on the screen.
So, why iterating the map to show all icons don't work?
The problem is that you’re not returning anything from showIcons. Either you remove { } from there
const showIcons = () =>
[...getIconsMetadata().values()].map((iconType, index) => {
const iconComponent = getMyIcon(iconType);
return <View key={index}>{iconComponent}</View>;
});
or add return before [...getIconsMetadata().values()].map
const showIcons = () => {
return [...getIconsMetadata().values()].map((iconType, index) => {
const iconComponent = getMyIcon(iconType);
return <View key={index}>{iconComponent}</View>;
});
};

How to pass function between multiple components in React Native

I have problem and I didnt find answer, so I try anyone who could help with:
I have main app component:
export default class App extends React.Component {
constructor(props) {...}
openSettings = () => { ....some changing state}
render(){
return (...
<Settings openSettings={() =>this.openSettings()}/>
);}
...};
My Settings file :
const Settings = ({openSetting}) => {
return (
<MyHeader openSetting={openSetting}></MyHeader>...
)}
export default Settings;
And I want to pass openSetting to another file:
const MyHeader = ({openSetting}) => {
return (
<Text onPress={openSetting}>Open it!</Text>
)}
export default MyHeader ;
Anyone knows why it doesnt work?
Its probably the typo on your code thats causing this issue, and its not working.
You created a function named openSettings, but you sent the props as openSetting. Try writing the prop name correctly without typo and it should work.
export default class App extends React.Component {
constructor(props) {...}
openSettings = () => { ....some changing state}
render(){
return (...
<Settings openSettings={() =>this.openSettings()}/> //<== Check this line
);}
...};
const Settings = ({openSettings}) => { //<== Check this line
return (
<MyHeader openSettings={openSettings}></MyHeader>... //<== Check this line
)}
export default Settings;
const MyHeader = ({openSettings}) => { //<== Check this line
return (
<Text onPress={openSettings}>Open it!</Text> //<== Check this line
)}
export default MyHeader ;

How to access value calculated in `useEffect` hook from renderer

I am developing a React-Native project with functional component.
Here is a very simple screen which renders a calculated result list. Since I need to calculation to be called only once so I put it inside the useEffect hook.
import {doCalculation} from '../util/helper'
const MyScreen = ({navigation}) => {
useEffect(() => {
// I call a function from a helper module here.
// The result is a list of object.
const result = doCalculation();
// eslint-disable-next-line
}, []);
// renderer
return (
<View>
// Problem is 'result' is not accessible here, but I need to render it here
{result.map(item=> <Text key={item.id}> {item.value} </Text>)}
</View>
)
}
export default MyScreen;
As you can see I have called the doCalculation() to get the result inside useEffect hook. My question is how can I render the result in the return part? Since the result is calculated inside the hook, it is not accessible in the renderer.
P.S. Moving the const result = doCalculation() outside the useEffect hook is not an option since I need the calculation to be called only once.
Below is an example. According to the above comments it looks like you want it to be called once on component mount. All you really need to do is add a useState
import {doCalculation} from '../util/helper'
const MyScreen = ({navigation}) => {
const [calculatedData, setCalculatedData] = useState([])
useEffect(() => {
// I call a function from a helper module here.
// The result is a list of object.
const result = doCalculation();
setCalculatedData(result)
// eslint-disable-next-line
}, []);
// renderer
return (
<View>
// Problem is 'result' is not accessible here, but I need to render it here
{calculatedData.map(item=> <Text key={item.id}> {item.value} </Text>)}
</View>
)
}
export default MyScreen;
const [calculatedData, setCalculatedData] = useState([])
useState is a hook used to store variable state. When calling setCalculatedData inside the useEffect with empty dependency array it will act similar to a componentDidMount() and run only on first mount. If you add variables to the dependency array it will re-run every-time one of those dep. change.
You can change the data inside the calculatedData at anytime by calling setCalculatedData with input data to change to.
Make use of useState to save the calculation result and then use the variable inside return. See https://reactjs.org/docs/hooks-state.html.
Code snippet:
import {doCalculation} from '../util/helper'
const MyScreen = ({navigation}) => {
const [result, setResult] = useState([]);
useEffect(() => {
// I call a function from a helper module here.
// The result is a list of object.
const tempRes = doCalculation();
setResult(tempRes);
// eslint-disable-next-line
}, []);
// renderer
return (
<View>
// Problem is 'result' is not accessible here, but I need to render it here
{result.map(item=> <Text key={item.id}> {item.value} </Text>)}
</View>
)
}
export default MyScreen;
Is async function?
if the function is not async (not wating for respond like from api) - you don't need useEffect.
import React from 'react';
import { Text, View } from 'react-native';
import {doCalculation} from '../util/helper'
const results = doCalculation();
const MyScreen = () => {
return (
<View>
{results.map(item=> <Text key={item.id}> {item.value} </Text>)}
</View>
)
}
export default MyScreen;
else you should wait until the results come from the server..
import React, { useState, useEffect } from 'react';
import { Text, View } from 'react-native';
import { doCalculation } from '../util/helper';
const MyScreen = () => {
const [results, setResults] = useState(null) // or empty array
useEffect(() => {
(async () => {
setResults(await doCalculation());
})();
}, []);
return (
<View>
{results?.map(item => <Text key={item.id}> {item.value} </Text>) || "Loading..."}
</View>
)
}
export default MyScreen;
and I can use more readable code:
if (!results) {
return <View>Loading...</View>
}
return (
<View>
{results.map(item => <Text key={item.id}> {item.value} </Text>)}
</View>
)
the async function can be like:
const doCalculation = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve([{ id: 1, value: 1 }]);
}, 2000);
});
};

State changes in a react application with mobX

My task is to show the download component when data from the server has not yet arrived.
export const LoaderComponent = () => (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
const styles = StyleSheet.create({
center: {
.....
},
});
I created a state file to display the boot component.
import { observable, action } from 'mobx';
class LoaderState {
#observable loading: boolean = true;
#action showLoader() {
this.loading = true;
}
#action hideLoader() {
this.loading = false;
}
}
export default new LoaderState();
When authorizing the user, I display the download component, after receiving data from the server, I hide the download component. I made an artificial delay of two seconds.
class AuthState {
#observable email: string = '';
#observable password: string = '';
#action authentication(data: IAuth) {
console.log('Action authentication');
LoaderState.showLoader();
....
setTimeout(() => {
LoaderState.hideLoader();
console.log('Change state loader', LoaderState.loading);
}, 2000);
}
}
export default new AuthState();
On the next screen, I check if the download flag is set, I show the download component, and if not, I hide it.
export const ProvidersScreen = () => {
console.log(LoaderState.loading);
if (LoaderState.loading) {
return <LoaderComponent />;
}
return (
<View>
....
</View>
);
};
The problem is that the download component is always shown and when the state changes, it is not hidden. Why is the download component not hiding?
I think the reason is your ProvidersScreen is not an observer component, so try it:
export const ProvidersScreen = observer(() => {
console.log(LoaderState.loading);
if (LoaderState.loading) {
return <LoaderComponent />;
}
return (
<View>
....
</View>
);
});
You forgot to add observer
Add below code:
import { observer } from "mobx-react";
export const ProvidersScreen = observer(() => {
console.log(LoaderState.loading);
if (LoaderState.loading) {
return <LoaderComponent />;
}
return (
<View>
....
</View>
);
});

React Native: Conditionally Render Components

I've searched this question and found a solution that said to conditionally render based on the state as follows:
render() {
const content = this.state.isReady ? <Home/> : <Splash/>;
return (
{content}
);
}
However, I keep getting an Invariant Violation: Objects are not valid a React child (found object with keys {content}.
your make typo, you returned Object, instead use between JSX elements:
const Ready = () => <div>Ready</div>
const NotReady = () => <div>NotReady</div>
class App extends Component {
constructor() {
super();
this.state = {
isReady: false
};
}
render() {
const content=this.state.isReady ? <Ready /> : <NotReady />
return (
<div>
{content}
</div>
);
}
}
Use simple if else instead of ternary expression because sometimes ternary operators "return" whatever's inside and can't execute blocks of code.
if (this.state.isReady) {
return <Home />
} else {
return <Splash />
}