AsyncStorage in redux thunk, action not dispatching - react-native

I am trying to use asynchStorage to dispatch actions when I recieve the data:
componentWillMount() {
AsyncStorage.getItem("loggedIn")
.then(this.props.dispatch(isLoadingCredentials(true)))
.then(
data =>
data
? this.props
.dispatch(setCredentials(JSON.parse(data)))
.then(this.props.dispatch(navigate("Month")))
.then(
this.props.dispatch(
isLoadingCredentials(false)
)
)
: this.props.dispatch(isLoadingCredentials(false))
);
}
Using redux connect
export default connect(mapStateToProps)(HomeScreen);
I get the error:
Possible Unhandled Promise Rejection (id: 0):
TypeError: _this2.props.dispatch(...).then is not a function
TypeError: _this2.props.dispatch(...).then is not a function
How can I dispatch actions when I receive the data?

Try defining mapDispatchToProps and passing it as a second argument, alongside mapStateToProps.
const mapDispatchToProps = dispatch => ({
isLoadingCredentials: (loadingCredentials) => (
dispatch(isLoadingCredentials(loadingCredentials))
)
})
You could use bindActionCreators as Pegase745 suggested. It's shorthand for the above.
Then pass it through to your component via connect.
connect(mapStateToProps, mapDispatchToProps)(HomeScreen)
You would then use your function like so, notice there's no dispatch property.
componentWillMount() {
AsyncStorage.getItem("loggedIn")
.then(() => this.props.isLoadingCredentials(true))
...
}
You probably want to refactor your call to AsyncStorage because you're calling isLoadingCredentials(true) once the Promise has been fulfilled.
componentWillMount() {
this.props.isLoadingCredentials(true);
AsyncStorage.getItem("loggedIn")
.then((user) => {
// You have your User, do as you wish
// Add setCredentials and others to mapDispatchToProps
})
.catch(() => {
this.props.isLoadingCredentials(false); // Error
});
}

Related

useState in React Native get data of previous state

I have one state
const [data, setData] = useState("");
And 2 useEffects that call in parallel when component renders
useEffect(() => {
socket.on("message",()=>{
console.log(data)
})
}, [socket])
useEffect(() => {
const res = getDataFromServer()
setData(res.data)
}, [isLoading])
2nd useEffect get data from server and set state but when socket arrive in first useEffect data is on initial state that is empty. How can I get updated state data in first useEffect when socket arrives. If I set data as dependency to first useEffect then socket event is reinitialized and callback is calling multiple times.
You can return a function in useEffect to clean unnecessary handlers / event listeners.
Effects with Cleanup - React Docs
In this function you can use the offAny method of socket.io client to remove previous listener.
useEffect(() => {
const currentListener = socket.on("message",()=>{
console.log(data)
});
return () => {
socket.offAny(currentListener);
};
}, [socket, data]);
This might help
React.useEffect(() => {
// This effect only executes on the initial render so that we aren't setting up the socket repeatedly.
socket.on("message",()=>{
console.log(data);
})
return () => {
socket.off('message',() => {});
}
}, []);

Async custom hook from within useEffect

When kept in the component body, the following code works fine. Inside useEffect, it checks the asyncstorage and dispatches an action (the function is longer but other checks/dispatches in the function are of the same kind - check asyncstorage and if value exists, dispatch an action)
useEffect(() => {
const getSettings = async () => {
const aSet = await AsyncStorage.getItem('aSet');
if (aSet) {
dispatch(setASet(true));
}
};
getSettings();
}, [dispatch]);
I'm trying to move it to a custom hook but am having problems. The custom hook is:
const useGetUserSettings = () => {
const dispatch = useDispatch();
useEffect(() => {
const getSettings = async () => {
const aSet = await AsyncStorage.getItem('aSet');
if (aSet) {
dispatch(setASet(true));
}
};
getSettings();
}, [dispatch]);
};
export default useGetUserSettings;
Then in the component where I want to call the above, I do:
import useGetUserSettings from './hooks/useGetUserSettings';
...
const getUserSettings = useGetUserSettings();
...
useEffect(() => {
getUserSettings();
}, [getUserSettings])
It returns an error:
getUserSettings is not a function. (In 'getUserSettings()', 'getUserSettings' is undefined
I've been reading rules of hooks and browsing examples on the internet but I can get it working. I've got ESlint set up so it'd show if there were an invalid path to the hook.
Try the following.
useEffect(() => {
if (!getUserSettings) return;
getUserSettings();
}, [getUserSettings]);
The hook doesn't return anything, so it's not surprising that the return value is undefined ;)

Nuxt Vuex mutation runs but doesn't update state

I'm trying to get some data by nuxtServerInit and save it in state
store/index.js
import { fireDb } from '~/plugins/firebase'
export const state = () => ({
posts: []
})
export const mutations = {
addPosts (state, post) {
state.posts.push(post)
console.log('mutation =>', state.posts.length)
}
}
export const actions = {
nuxtServerInit (state, ctx) {
fireDb.collection('posts').orderBy('timestamp', 'desc').limit(3).get().then((snapshot) => {
snapshot.forEach((doc) => {
state.commit('addPosts', doc.data())
})
console.log('action => ', state.posts.length)
})
}
}
when I run this code console output is
mutation => 1
mutation => 2
mutation => 3
ERROR Cannot read property 'length' of undefined
And vue dev tools also doesn't show there's data inside posts[].
What am I missing here?
It looks like nuxtServerInit is dispatched as an action with the Nuxt context. Being an action, the first argument will be the Vuex context.
The Vuex context exposes several properties including state and commit.
The docs also say:
Note: Asynchronous nuxtServerInit actions must return a Promise or leverage async/await to allow the nuxt server to wait on them.
You can change your code to:
async nuxtServerInit({state, commit}, ctx) {
let snapshot = await fireDb.collection('posts').orderBy('timestamp', 'desc').limit(3).get();
snapshot.forEach((doc) => {
commit('addPosts', doc.data())
});
console.log('action => ', state.posts.length)
}

redux-thunk: actions are not dispatching

I am trying to build an app in react native that is suppose to take take two inputs by a user and then make a query to an api and get information about the two inputs. I have been having trouble with redux and redux-thunk and specifically with async actions.
This is the code in my app that i am specifically having trouble with
export const fetchData = url => {
console.log("start Fetching");
return async dispatch => { // this is where the problem is
dispatch(fetchingRequest());
try {
const response = await fetch("https://randomuser.me/api/?results=10");
const json = await response.text();
if (response.ok) {
dispatch(fetchingSuccess(json));
console.log("JSON", json);
} else {
console.log("fetch did not resolve");
}
} catch (error) {
dispatch(fetchingFailure(error));
}
};
console.log("Fetched data");
};
Upon debugging the function, I have ended with finding that when the fetchData function is called the function will execute but the async dispatch that is being returned has undefined behavior.
The output in the debugger when the function is called should be
start Fetching
JSON file information/Error
but the output in the debugger is actually
start Fetching
This is the function in which fetchData is called in
_onPress = () => {
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
this is the mapDispatchToProps function that I have added. The problem is i do not know what to add inside the function.
const mapStatetoDispatch = (url, dispatch) => {
return {dispatch(fetchData(url))}; // do not know what to place in body of function
};
i have connected it in the component with
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
these are the action creators that I import, if needed
import {
fetchingSuccess,
fetchingRequest,
fetchingFailure,
fetchData
} from "../data/redux/actions/appActions.js";
Assuming you have added redux-thunk as a middleware, it looks like the errors are here:
_onPress = () => {
const { fetchData } = this.props;
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
and
const mapStatetoDispatch = dispatch => ({
fetchData: url => dispatch(fetchData(url)),
}};

I am getting this error: actions must be plain objects. Use custom middleware for async actions

I'm getting the error:
Actions must be plain objects. Use custom middleware for async actions.
I've tried the solution in the following Stack Overflow question, but it didn't work:
React-Redux: Actions must be plain objects. Use custom middleware for async actions
action
export async function signupp(data){
console.log('In signupp:');
try{
const request = await axios({
method:'POST',
url:'http://192.168.1.10:3003/users/signup',
data:{
email:data.email,
password:data.password
},
}).then(response=>{
console.log(response.data);
return response.data
}).catch( e => {
console.log(e);
return false
});
return {
type:'signup',
payload:request
}
}
catch(e) {
console.log(e);
return false;
}
}
reducer
export default function(state={},action){
switch(action.type){
case 'signup':
return {
...state,
auth:{
email: action.payload.email,
password:action.payload.password
}
}
}
}
store
const createStoreWithMiddleware = applyMiddleware()(createStore);
const appRedux = () => (
<Provider store = {createStoreWithMiddleware(reducers)}>
<App/>
</Provider>
)
AppRegistry.registerComponent(appName, () => appRedux);
BTW, I am getting the right response in the log.
Inside of the component, in the place where you call signupp function, you have mapDispatchToProps function as callback in connect function from react-redux lib, which is doing behind the hoods something like dispatch(signupp())(or maybe you are doing dispatch directly without react-redux lib).
According to redux API, this dispatch function expects to receive a plain object, but your signupp() function returns a promise(as you have async inside).
To solve this problem you can simply use redux-thunk middleware. Also you can see some examples in the redux docs section about async actions.
An alternative solution could be to move fetch logic to component and then dispatch just plain object with data that you received from the request.