I am creating react-native app. I have an array and want to use value outside the looping. I have declared value in this.state={current_cat_id:'',} I have tried it in componentWillMount like:
componentWillMount() {
var ids = [];
this.props.data.map((dataImage,Index) => {
dataImage['main-head'].map((subchild,Index2) => {
ids.push(subchild['path'])
})
})
this.setState({current_cat_id: ids})
}
its returning blank page. is this right approch
it should work for you. try this:-
componentWillMount() {
var ids = [];
this.props.data.map((dataImage) => {
dataImage['main-head'] != undefined && dataImage['main-head'].map((subchild) => {
ids.push(subchild['path'])
})
})
this.setState({current_cat_id: ids})
}
componentWillMount is called before the render method is executed. It is important to note that setting the state in this phase will not trigger a re-rendering. Avoid introducing any side-effects or subscriptions in this method. Use componentDidMount() instead.
Related
I am new in React-Native, I am facing one issue in my app and not able to resolved that yet. All thing is working but first time API being calling two time unnecessary.
Here is my useEffect method :-
useEffect(() => {
if (transactions.length === 0) {
setIsAPICall(true);
}
getTransactions().then((response: any) => {
dispatch(actionTransitionsBadge(0));
setTransactions(response);
setIsAPICall(false);
});
}, [user, onChainBalance]);
In this method I have to get transaction list when component first time open. After that I have to refresh this list when user and onChainBalance get updated. The thing is working but when I am loading this component first time then the api is calling multiple time.
What I can do to manage this flow that once component load then api call once after then when my two state changed the api call again.
Put your getTransactions in the useCallback, like this:
const fetchData=useCallback(
() => {
getTransactions().then((response: any) => {
dispatch(actionTransitionsBadge(0));
setTransactions(response);
setIsAPICall(false);
});
},
[],
)
Then call fetchData in the useEffect
Here is some Details where you can useEffect .
useEffect(() => {},)
useEffect(() => {},[])
useEffect(() => {},[dependency_array])
Here I am explaining them one by one
The first would call on every render
The Second would call two time
the third would call two times and when the dependency array changes
Here is your use Case
useEffect(() => {
if(user !== null && user !== undefined && transactions.length === 0){
getTransactions().then((response: any) => {
dispatch(actionTransitionsBadge(0));
setTransactions(response);
setIsAPICall(false);
});
}
}, [user, onChainBalance]);
but Still this is not a good method. you should use react-query or redux-toolkit-query
I declared and initialized the variable like this:
const [user, setUser] = useState(null)
Then make a function like this:
const getUser2 = async () => {
try {
const user2 = await AsyncStorage.getItem('user')
let parsed = JSON.parse(user2)
setUser(parsed)
console.warn('1')
} catch(err) {}
}
Then call it like this:
useEffect(() => {
getUser2()
return () => getUser2()
})
The problem is when I run it, it produces an infinite loop like this:
Why does it loop infinitely?
Add dependency array, Then it will get called only once -
useEffect(() => {
getUser2()
return () => getUser2()
}, [])
Go through this reference for better understanding of useEffect - https://blog.logrocket.com/guide-to-react-useeffect-hook/
In your useEffect you haven't added any dependency array, so on each re-render this useEffect is getting called, when you add [ ] dependency as a parameter to useEffect then it will act as componentDidMount which get called only once.
useEffect will be triggered every time the setState is set, if you do not pass the second parameter
I created a map for the array of exercises in my database, and then for each exercise, which is a document reference, I'm getting the data from that document reference and setting it to a state. This is resulting in an infinite loop right now.
If I remove the setExerciseData line, the console logs the exercise object's data that I'm expecting to see. I'm really not sure what the correct way to render the name field from this data is.
{workout.exercises.map((exercise) => {
async function getData(exercise) {
getDoc(exercise).then((doc) => {
console.log(doc.data());
setExerciseData(doc.data());
});
}
getData(exercise);
return (
<Text>{exerciseData.name}</Text>
)
})}
You need to use useEffect() and setState() to be able to render your data. Also, Firebase Firestore is Asynchronous in nature, as a general note, no one should be trying to convert an Async operation into a sync operation as this will cause problems. You need to use an Asynchronous function to fetch data from Firestore. See sample code below:
const getExerciseData = async () => {
const docRef = doc(db, "<collection-name>", '<document-id>')
const docSnap = await getDoc(docRef)
if (docSnap.exists()) {
// console.log("Document data:", docSnap.data())
setExerciseData(docSnap.data())
} else {
// doc.data() will be undefined in this case
console.log("No such document!")
}
}
useEffect(() => {
getExerciseData()
}, [])
return (
<Text>{exerciseData.name}</Text>
)
You could also check my answer on this thread for more use-cases.
I got 3 pages
homepage, productList and productDetails
When going from homepage to productList I pass a route param,
navigation.navigate('productList', { showCategory: 'productListA'} )
InitialProcess when component mounted
Inside the productList page when the component is mounted. I am declaring use state like this.
const {showCateory} = route.params;
const [activeTab, setActiveTab] = useState(showCateory);
and calling api using that activeTab
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
async function fetchData() {
try {
await dispatch(
fetchProductList(
activeTab,
),
);
} catch (err) {
console.log(err);
}
}
fetchData();
});
return unsubscribe;
}, []);
User Interaction
But I also add the button in the productList so that user can change the current active tab
<TouchableOpacity onPress={() => changeTab()}></TouchableOpacity>
const changeTab = async () => {
await setActiveTab('productListB'),
await dispatch(fetchProductList(activeTab)
}
Take note that right now active tab and data coming from api is different from when the component is start mounted.
Navigation Change again
When use goes from productList to productDetails. All thing is fine.
But inside the product details I am going back to productList with this.
navigation.goBack().
When I am back in productList page The activeTab is change back to productListA and the data is change back to when component is mounted
Can I pass or change the route params when calling navigation.goBack()?
add activeTab in useEffect depedineces.
as docs say
The array of dependencies is not passed as arguments to the effect function. Conceptually, though, that’s what they represent: every value referenced inside the effect function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically.
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
async function fetchData() {
try {
await dispatch(
fetchProductList(
//this value will always updated when activeTab change
activeTab,
),
);
} catch (err) {
console.log(err);
}
}
fetchData();
});
return unsubscribe;
}, [activeTab]); //<<<<< here
also you need to know setState() does not always immediately update the component. see here
so change this
const changeTab = async () => {
//await setActiveTab('productListB'),
//await dispatch(fetchProductList(activeTab)
setActiveTab('productListB')
dispatch(fetchProductList('productListB'))
}
This might be happening because route.params is still set to { showCategory: 'productListA'} when you are coming back to the screen.
If this is the case, you can fix it by Changing params object in changeTab() like
navigation.setParams({
showCategory: 'productListB',
});
I hope this will fix your problem.
This happens because the callback function inside the focus listener uses the initial value of the state when the function was defined (at initial page render) . Throughout the lifespan of listener the callback function uses this stale state value.You can read more about this behaviour in this answer
Although the answer by Ahmed Gaber works in this case as the listener is cleared and redefined after each state change.Another common work-around is to use an useRef instead of useEffect.A ref is basically a recipe that provides a mutable object that can be passed by reference.
In your case you can initialise activeTab with navigation param value using useRef hook as :
const activeTab = useRef(showCateory);
and the focus listener callback function should be changed to use the Reference current value as
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
async function fetchData() {
try {
await dispatch(
fetchProductList(
activeTab.current, //<<<<<<---------here
),
);
} catch (err) {
console.log(err);
}
}
fetchData();
});
return unsubscribe;
}, []);
and the changeTab function can directly update reference current value
const changeTab = async () => {
setActiveTab.current = 'productListB';
dispatch(fetchProductList('productListB'))
}
i have a useEffect function where a redux action is called and data is written to prop. My Problem is that useEffect loop many times and flooded the server with requests.
const { loescherData, navigation } = props;
useEffect(() => {
AsyncStorage.getItem('userdata').then((userdata) => {
if (userdata) {
console.log(new Date());
console.log(userdata);
var user = JSON.parse(userdata);
props.fetchLoescherDetails(user.standort);
setData(props.loescherData);
}
});
}, [loescherData]);
if i leave it blank the rendering is finished before receiving data and the content would not updated.
is there another way to work with this function?
loescherData won't be available right after calling your redux-action fetchLoescherDetails ... and changing component by setData will cause an infinite rendering cause your current useEffect has a dependency on loescherData
So I'd suggest you exec your redux-action onComponentDidMount by passing an empty-deps [] to your effect ... and then consume the output of you action in a different effect
useEffect(() => {
AsyncStorage.getItem('userdata').then((userdata) => {
if (userdata) {
console.log(new Date());
console.log(userdata);
var user = JSON.parse(userdata);
props.fetchLoescherDetails(user.standort);
// setData(props.loescherData);
}
});
}, []);
useEffect(() => {
if (loescherData) {
// do some with loescherData like setState
}
}, [loescherData]);