I saw some tutorials online and majority of the examples are using
a class component e.g
export default class App extends Component {
constructor() {
super();
//set state here and data source
}
componentDidMount() {
//get json data here
}
render() {
return(
blah blah blah
)
}
}
what if I want a sub component to have a functionality that calls fetch , how to do it ?
//NewComponent.js
//this is my sub component
const NewComponent = () => {
return(
<FlatList />
//I want a list of data here
)
}
const getData = () => {
//call fetch here
}
export default NewComponent
so in my second code snippet how to call the getData in order for me to display the data inside the NewComponent?
You have several options. Personally I would go for #3
You can make your sub component a class and use React's lifecycle method such as componentDidMount
You can move getData function to another javascript module. Then import and call it from parent component. Pass the result as props down into the sub component.
Use React's Effect Hook. Pay attention to the section Optimizing Performance by Skipping Effects also.
Hope that helps.
Related
I'm kinda new to React Native. I'm using the getFamily() on my screen MyFamily but when I go to another screen there change the value of the Family and come back to my MyFamily screen then I don't see the changes.
I tried doing it with the useEffect but still nothing happens, also the log doesn't happen. How can I solve this?
export default function MyFamily({ navigation, props, person, inheritors }) {
console.log(getFamily());
let [family, setFamily] = useState(getFamily());
useEffect(() => {
console.log(getFamily());
setFamily(getFamily());
}, [getFamily]);
In the screen where I set the Family again I do this:
And I know that's correct because the Json that is shown shows the updated value.
import { setFamily } from '../../utilities/family';
setFamily(responseJson.family);
This is the way family is formulated:
let family = '';
export default family;
export function getFamily() {
return family;
}
export function setFamily(f) {
family = f;
}
React doesn't actually know that the value returned from the getFamily function changes each render. In the useState function, it's only used in the initial state, and the useEffect function never gets re-run because the getFamily function itself doesn't ever change and re-trigger the useEffect. You have to change the getFamily() function to use a state that's stored in a parent component and pass it into the MyFamily component as a prop.
e.g.
// the parent component that renders the MyFamily screen
function Router() {
const [family, setFamily] = useState('')
return (
<Navigator>
<Screen component={<MyFamily family={family} setFamily={setFamily} />
<Screen component={<OtherComponent family={family} setFamily={setFamily} />
</Navigator>
}
)
}
And then from MyFamily:
function MyFamily({ family }) {
console.log(family); // this should be updated
}
and from OtherComponent:
function OtherComponent({ setFamily }) {
return (<Button onClick={() => setFamily('newFamily')>Change family</Button>)
}
hi everyone i'm just learning react native and i have a question. How do I wait for an asynchronous method to finish and then pass the result to another screen?
My code is:
export default class AAA extends Component {
constructor(props){
super(props);
this.state={
comments: [],
datetime: []
}
}
//getPost is a network call which gets and store the result in the state of the class
async getPost(){
const utils=new Utils();
const responseJson = await utils.getPost("ok","yes")
const comment = (responseJson?.posts ?? []).map((data) => data.comment)
this.setState({comments:comment})
console.log("now i change state with new value")
}
componentDidMount(){
this.getPost()
}
render(){
return(
<NextScreen
comment={this.state.comments}
/>
)
}
}
I want the getPost () method to finish and then go to the other screen, passing the result of the getPost () method. I tried to use the componentWillMount method as well but it is deprecated. How can I do?
You can use conditional rendering in this case as follows:
render() {
if (this.state.comments.length === 0) {
return null;
}
return (
<NextScreen
comment={this.state.comments}
/>
)
}
The initial state comments contains an empty array, thus it is never undefined. However, its length is equal to zero until the async call returns with its result.
You can use react-router params
if you are fetching data in the splash screen, you want to pass it to the next page.
in then() or after await expression
you can learn about this in React router, pass data when navigating programmatically?
I would like to know how to manage state property when the component mounts and unmounts.
I have a lot of different components in my application to maintain the application flow. I know about function componentdidmount and componentWillUnmount. and I also tried the solution about _isMounted=true on componentdidmount function and check _isMounted properties value when I update setState and then update _isMounted=false on componentWillUnmount function.
but this won't work when more two components come in the picture.
For example following links:
https://www.robinwieruch.de/react-warning-cant-call-setstate-on-an-unmounted-component/
Is there a way to check if the react component is unmounted?
as per the example, I have made a common class which will update the value of a component in setMounted function and will return value in getMounted function to validate component is mounted or not. These methods work correctly on a single screen when I call another screen from a stack and update some values then comes back on the previous page and refresh page it will ismount=false.
class Mount {
isMounted=false;
getMounted=()=>{
return isMounted;
}
setMounted=mounted=>{
isMounted=mounted;
}
}
var mount=new Mount();
export default mount;
class example extends component{
componentDidMount=async()=>{
mount.setMounted(true);
await this.loadScreen();
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
async() => {
await this.loadScreen();
}
);
}
loadScreen=async()=>{
//some other stuff
if(mount.getMounted()){//second time value is false
this.setState({value:'value'});
}
}
componentWillUnmount() {
mount.setMounted(false);
}
//renderview where i call example2 on buttonclick
}
class example2 extends component{
componentDidMount=async()=>{
mount.setMounted(true);
await this.loadScreen();
}
loadScreen=async()=>{
//some other stuff
if(mount.getMounted()){
this.setState({value:'value'});
this.props.navigation.goBack();
}
}
componentWillUnmount() {
mount.setMounted(false);
this.willFocusSubscription.remove();
}
}
It was showing following warning before using mount functions:
Can't perform a React state update on an unmounted component
You are creating only a single instance of your Mount class that is exported and shared across every instance of every component. You will need to create a new instance of Mount for each component instance:
class Mount {
...
}
// export the Mount class
export default Mount;
class example extends component{
constructor(props) {
super(props);
// create an instance of Mount for each component instance
this.mount = new Mount();
}
componentDidMount=async()=>{
this.mount.setMounted(true);
await this.loadScreen();
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
async() => {
await this.loadScreen();
}
);
}
loadScreen=async()=>{
//some other stuff
if(this.mount.getMounted()){//second time value is false
this.setState({value:'value'});
}
}
componentWillUnmount() {
this.mount.setMounted(false);
}
//renderview where i call example2 on buttonclick
}
Notice the addition of the constructor and the use of this.mount instead of mount throughout.
Screen-test.js
it('renders the Engagement Detail modal', () => {
const tree = renderer.create(<EngagementDetailModal details={engagementData}/>).toJSON();
expect(tree).toMatchSnapshot();
})
the component I am trying to test
class CompanyDetailView extends React.Component {
render() {
const data = this.props.navigation.getParam('details');
return (
// rest of the code
)
}
}
My data variable is just a bunch of static data. Using jest I am getting this error TypeError: Cannot read property 'getParam' of undefined and it is pointing to this line const data = this.props.navigation.getParam('details');
I know the component works as i run the app, just the test is failing.
I thought that by just providing the prop to the component in my Screen-test.js file it would work the same but i get undefined. How can i test this?
Also I am passing the prop using react navigation like this onPress={() => this.props.navigation.navigate('EngagementDetailModal', {details: this.props.data})
It cannot read the property getParamof navigation because you haven't mocked the navigation. So when the test case runs, it doesn't know what this.props.navigation does.
Add the following after imports section in your Screen-test.js .
const testProps = props => ({
navigation: {navigate: jest.fn(), getParam : jest.fn()},
...props,
})
Hope this helps!!
I'm new on React-Native and it's my first React-Native app. However, I have already some problems.
I want to pass a variable from one class (Home.js) to an another. (Is it possible without using the composent in the render() fonction ?)
##### Home.js #####
class Home extends Component {
constructor(props) {
super(props);
this.state = {direction: "defaultvalue"};
}
getCurrentDirection() {
return this.state.direction;
}
render() {
/***..... some elements ..*/
}
}
export default Home
And
#### Two.js ####
import Home from './Home'
/** SOME CODE **/
const DrawerOptions = {
initialRouteName: Home.getCurrentDirection(),
contentComponent: CustomDrawerContentComponent,
drawerWidth: 300,
};
However it doesn't work... How to resolve it ? I have already try some solutions as declare the getCurrentDirection as static but nothing.
In addition, it seems to be a specific case because DrawerOptions is not a class. Could you please, add to your response also, how make it if I want to obtain the variable into the class Two.js ?
I meant if Two.js was for example :
##### Two.js #####
class Two extends Component {
var myvariable = Home.getCurrentDirection();
render() {
/***..... some elements ..*/
}
}
Thanks a lot in advance
A recommendable way of accessing the state from a component into another is to use (in this case) the Home component as a parent of Two component. This way you don't have to trigger a function to access the Home's state. On each time when the state of the parent (in this case) component will be updated, the Two component will receive the updated property (direction). If you want to call a function from Two component, you have to pass it a function as a property (changeCurrentDirection) that will call back the function you want to trigger from Home component.
So you would have something like this:
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
direction: "defaultValue"
};
}
changeCurrentDirection() {
this.setState({
direction: "valueChanged"
})
}
render() {
let state = this.state;
return (
<Two
direction={state.direction}
changeCurrentDirection={() => this.changeCurrentDirection.bind(this)}/>
)
}
}
class Two extends React.Component {
render() {
let props = this.props;
return (
<div>
<h3>{props.direction}</h3>
<button onClick={props.changeCurrentDirection()}>Change value</button>
</div>
)
}
}
React.render(<Home/> , document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.0/react.min.js"></script>
<div id="app"></div>
Additional info you can find here.
Also, if you want to have a good management of the state of your components, my advice for you is to use redux. Using this library you can easily connect the component's actions and properties that can further be accessible from other files where you can manage them.