react native Redux:Even though I have used useDispatch I still do not have access to methods in the action - react-native

I've used dispatch in my component like following but I get the error modifySeatMap is not defined .But I do have modifySeatMap function defined in one of my action files. Why is this showing modifySeatMap not defined yet?
const dispatch = useDispatch();
const changeStatus=(seat)=>{
console.log("change status called with seat",seat)
seat.status="selected"
dispatch(
modifySeatMap(props.seatMap))
}
I'm attaching the screenshot of the directory structre and the method inside the action here
Why is modifySeatMap undefined here?

From your code, I do not see any issues.
I expect the issue is in how you apply your middleware.
It should look something like this.
export default store = createStore(rootReducer, applyMiddleware(thunk))

Related

Vue 3 composition API, undefined variable, lifecycle

I am quite new to Vue.js and while developing my first project i have stumbled upon a problem which I think is related to the component's lifecycle.
The context is the following:
I am using Vue 3 with composition API.
I have a "Map" component in which I use d3.js to show a chart.
In my Setup() method I have onBeforeMount() and onMounted().
In onBeforeMount() method, I want to use data from my Firestore database for the values in the chart. The console.log on line 47 shows the data correctly.
In onMounted(), is where I plan to put my d3 chart. But when I try to access the data as in console.log on line 55, I get undefined...
Ideally, from "Map" component, I want to fetch the data from my database and use it in order to create the chart and then have the component render the chart.
Then I would have another component called 'MapSettings" that will be able to alter the data in "Map" such as filters etc...and have the values automatically updated in the chart.
Finally, both components will be siblings and have same parent component. So "Map" and "MapSettings" are on same hierarchical level.
But first I need to understand why I am getting undefined.. Any help or suggestion would be greatly appreciated!
Your lifecycle hooks look nice. The problem that you're facing has to do with code been executed asynchronously and code been executed synchronously.
You're declaring a function that uses async-await feature. This function will be executed asynchronously. In this case, you're getting data from Firestore and storing it in a ref at onBeforeMount().
On the other hand, you're declaring a normal function at onMounted() trying to access a ref's value, which will result in undefined because the function that you define at onBeforeMount() didn't finish its execution (or it's in the event queue) when onMounted is called.
That's why you first see the console.log that comes from onMounted.
One solution would merge both functions in one lifecycle hooks and use async await:
setup() {
const {actorDocs, load} = getActorDocs()
const actorsData = red([])
// load actor data from db
onBeforeMount( async () => {
await load()
actorsData.value = actorDocs
console.log(actorsData.value)
// manipulate it here...
})
}
Keep in mind that you cannot pause with async-await a lifecycle hook. What you're pausing is the function that Vue will execute in that hook. Indeed, this is really nice because pausing a hook wouldn't be efficient at all.
i face that problem, in my case i want use imgsRef.value out of onBeforeMount scope. How to get imgsRef value out of beforeMount scope?
onBeforeMount( async () => {
await axios
.get("http://localhost:3000/ourMoment")
.then((response) => {
imgsRef.value = response.data
// imhsRef get the value from api
console.log(imgsRef.value.photo)
})
})
// i try to concole.log here but the value still empty
console.log(imgsRef.value)

React life cicle and AsyncStorage

I'm creating an app which passes some crucial info via AsyncStorage, but now have a problem when updating it on another screen.... Let's see:
On Screen 1 :
Load data from AsyncStorage on componentDidMount
componentDidMount() {
AsyncStorage.getItem("userChats").then((value) => {
this.setState({userChats: JSON.parse(value)});
});
}
then on Screen 2 I modify userChats.....
I'll like that when coming back again to Screen 1, the changes made on Screen 2 be reflected on Screen 1 but there are NOT as componentDidMount is not trigged again...........
What's the correct way to do it?
Thanks
componentDidMount is a life-cycle method. Navigating from Screen1 to Screen2 does NOT unmount Screen1. So, when you come back from Screen 2 to Screen 1, the Screen 1 does not mounting because it was NOT unmounted. Hence, componentDidMount is not called.
Whats's the correct way of doing this?
You should use Context API. When you load from AsyncStorage, set that value to Context as well. When you update the value, write changes to both AsyncStorage and Context.
PS: The AsyncStorage may not needed. It depends on your requirement. Most probably, you will be able to achieve this only with Context API.
Please check the following snack. It is done using hooks. You can do the same using class components.
https://snack.expo.io/3L9QSqWqt
UPDATE:
If the data to be handled is too large, it is not recommended to use Context since it saves all the data in the device RAM and consuming too much RAM may result in app crash.
To do this without using context:
(1) Define a function to retrieve data from AsyncStorage.
loadData() {
AsyncStorage.getItem("userChats").then((value) => {
this.setState({userChats: JSON.parse(value)});
});
}
(2) Call it in componentDidMount.
componentDidMount() {
this.loadData()
}
(3) When navigating to the Screen2, pass a callback function as a prop to call loadData function.
this.props.navigation.navigate('Screen2', {
onGoBack: () => this.loadData(),
});
(4) Then in the Screen2, before goBack, you can do this:
await AsyncStorage.setItem('userChats', updatedData);
this.props.navigation.state.params.onGoBack();
this.props.navigation.goBack();
Then, the loadData function is called in the screen2.
PS: Since you use state to store the retrieved data from the AsyncStorage, you can also directly load them into the Context and use. But, keep in mind that, using too much of RAM may cause app crash.

Thunks not appearing in action list in React Native Debugger

I've done two React/Redux projects to date, the first using our own middleware and the most recent using redux-thunk. In the first we used "regular" action objects for our async actions, which our middleware interpreted. In that case, our actions understandably appeared in the action list in the react native debugger.
In the most recent project, however, I noticed that our thunks did not appear as actions in the debugger, which prompted the following questions:
1) Is this normal?
2) If so, do people find this not to be an issue?
3) Do the other async action libraries have the same problem?
Can you share your thunk with me? Thunks return functions that the thunk middleware passes dispatch to and calls. If you're dispatching an action inside of the function that the thunk returns, then you'll see a dispatch. It just won't be from the thunk. Example:
export const gotSomething = data => {
return {
type: GOT_SOMETHING,
data,
};
};
export const getSomething = () => { // <-- This is our thunk!
return async dispatch => {
const { data } = await axios.get('/something');
dispatch(gotSomething(data)); <-- This is the only thing being dispatched
};
};
The only thing that will truly dispatch here is GOT_SOMETHING.
So the order is like this:
You call a "dispatch" a thunk (but not really).
Redux (or the thunk middleware rather) will check to see if the thing you "dispatched" returns an object or a function.
If it's a function, that function is called and dispatch is provided to that function
That function dispatches some action (the thing you actually dispatched)
So even though you're are technically "calling dispatch" on your thunk, you're actually only dispatching the action inside of the function that your thunk returns. I hope this helps!

How to subscribe() to a combineReducer on Redux?

I`m using Redux on react-native with redux and react-redux. I use combinereducers() to combine two reducers:
const reducers = combineReducers({
sessiontype,
userdata
})
Now I subscribe to some change in my reducers:
store.subscribe((state, previousState) => {
//Do something
});
How I subscribe only to my userdata reducer? Because when I change the state of my sessiontype reducer I need to change the state of my userdata reducer (and It create a infinite cycle in my store.subscribe() because detect the whole reducer has modified.... (sorry my bad english)
The concept around Redux is that you have only one state and one reducer. The idea that you have separate reducers is just an implementation detail introduced by combineReducers() - it allows you to think of your reducers in parts, but to your application there's simply one reducer. Check out the discussion on this GitHub thread.
The mapStateToProps function inside the react-redux package allows you to select what parts of a reducer a component has access to. This may help you solve your issue. When a component with access to sessiontype updates the reducer, you can have that same component update userdata. To do that, you simply dispatch two separate actions to the store. Also, remember that subscribe does not actually give you access to the store - it simply let's you know that something has changed. Inside your subscribe function, you need to call getState() to actually read the data. Read more here.
Try something like this:
store.subscribe((state, prevState) => {
// run a check of conditions for an action being dispatched
// if you don't specify conditions, you'll end up in an
// infinite loop
if (stateHasFieldOrValue) {
// dispatch update to sessiontype
store.dispatch(updateSessionType(payload));
// dispatch update to userdata
store.dispatch(updateUserData(payload));
}
});

Why can't I access state in ComponentDidMount?

I have this code for React Native:
componentWillMount() {
shiftKeys = []; // need to clear previously set data or we will get dupicate array key errors
// get the current user from firebase
const userData = firebaseApp.auth().currentUser;
const profileRef = firebaseApp.database().ref('userdata').child(userData.uid);
profileRef.on('value', snapshot => {
if (snapshot.val().hasOwnProperty('licenseType') && snapshot.val().hasOwnProperty('licenseState') && snapshot.val().hasOwnProperty('distance')) {
this.setState({
licenseType: snapshot.val().licenseType,
licenseState: snapshot.val().licenseState,
distancePref: snapshot.val().distance,
});
console.log('State1', this.state.distancePref)
} else {
// redirect back to profile screens because we need three values above to search.
this.props.navigation.navigate('Onboarding1')
}
});
}
componentDidMount() {
console.log('State2', this.state.distancePref)
var geoQuery = geoFire.query({
center: [45.616422, -122.580453],
radius: 1000// need to set dynamically
});
I think this is some kind of scope issue?
When I look at the console log, State 1 is set correctly, but State 2 prints nothing.
In my app I need to look up a users distance preference, then use that to run a query.
How do I pass the value from componentWillMount to componentDidMount?
https://facebook.github.io/react/docs/react-component.html#the-component-lifecycle
setState in componentWillMount - bad way. You do not solve the problem this way, because state will not be updated until componentDidMount (see lifecycle). Check your condition when creating the state, in the constructor.
Or you can solve the problem using redux.
The root issue with this problem had to do with my not understanding how react and react native render the code.
I was trying to get users info from firebase, then set preferences, then use those preferences to run a search.
I added redux and thunk to handle the getting and saving of the users preferences separately from (and before) the user has a chance to search.