In Redux, how to get state in action? [duplicate] - react-native

This question already has answers here:
Accessing Redux state in an action creator?
(8 answers)
Closed 3 years ago.
In below example, after I get data from firebase, I want to add user object which is already present in redux store and append it in all the message objects. Request you to help.
Questions:
Is it a good approach to access state in action creator? If yes, how?
If not, what are the alternatives?
db.collection(`messages/${documentId}/chat`)
.get()
.then(snapshot => {
const messages = [];
snapshot.docs.forEach(doc => {
console.log("message", doc.id, doc.data());
messages.push({
id: doc.id,
from: doc.data().from,
to: doc.data().to,
text: doc.data().text,
timestamp: doc.data().timestamp.seconds * 1000
});
});
dispatch({ type: GET_CHAT, payload: messages });
});

if your using Redux Thunk middleware, you could simply do that:
const myAction = () => async (dispatch, getState) => {
const { field1, field2 } = getState().myReducer;
};

Simply.
import store from "..path/to/your/createStore/export".
then use it inside action or anywhere, where we cannot use connect method of redux to subscribe to our component.
store.getState() // will return the whole store object.
other useful method to dispatch action;.
store.dispatch(action name).
Lastly, soter.getState() should be used in case of emergency.
for your use case .
mostly we work with action arguments. And pass data as parameters of actions. From where we dispatch actions ex. from some component which is subscribed to redux store. there we will access to store object via the mapStateToProps method.
export const successBuy = data => {
let request = axios
.post(`/api/users/successBuy`, data)
.then(response => response.data);
return {
type: actionTypes.SET_CURRENT_USER,
payload: request
};
};

Related

Can I return value from action creator using redux-thunk?

I've seen a lot of examples of async action creators, but they all do some sort of fetching and pushing data to redux store and return nothing. I need another logic that looks something like:
const createUserAction = (user) => {
firestore().collection('users').add(user)
.then(result => {
dispatch({type: 'SET_USER', payload: {...user, id: result.id}})
})
}
I need to return result.id from createUserAction to navigate to page that displays user by his id. In my imagine it should work like
createUserAction({name: John}).then(id => navigation.navigate('UserDetailPage', {userId: id}))
I don't know how to implement that and I'll be glad if somebody can help
Returning values from action creators is a No-Go. The solution for this scenario that I've used and think is better is to do the redirect in the async action itself:
// afterCreation = callback function with one argument, the created user
const createUserAction = async (user, afterCreation) => {
const createdUser = await firestore().collection('users').add(user);
dispatch({type: 'SET_USER', payload: {...user, id: createdUser.id}});
afterCreation(createdUser);
};
createUserAction(
{name: John},
// Pass callback to action creator
(user) => navigation.navigate('UserDetailPage', {userId: user.id})
);

Using a state variable in Axios put request in React Native

I am modifying a state variable (setTokens(tokens - 1)) in React native and then using the modified variable in an axios PUT request (library_tokens: tokens).
The problem is, the put request is successful, but it puts the old value of the variable instead of the modified one in the database after the PUT request.
The following is the code:
const [tokens,setTokens] = useState(0)
const lendBook = book => {
// Add book to the IssueReturn collection in the database
issuesApi
.post('/bookissue', {
booksIssued: book._id,
image: book.image,
user: context.stateUser.user.userId,
})
.then(response => {
if (response.status === 200) {
// Decrement user's library token
setTokens(tokens - 1);
// Modify the user's library tokens
userProfileApi
.put(
`/${context.stateUser.user.userId}`,
{
library_tokens: tokens,
},
{
headers: {Authorization: `Bearer ${jwtToken}`},
},
)
.then(response => {
console.log('tokens', tokens);
});
}
})
.catch(err => {
console.log(err);
});
};
You can use useEffect like this.
useEffect(() => {
console.log(tokens) // do something after state has updated
}, [tokens])
After state updation the useEffect will be called, so you can call userProfileApi inside useEffect.
setTokens it not a synchron function. code executed afterwards will not have the updated state. either use a local variable to store the result of token - 1 or make use of the useEffect hook.

Where should I subscribe firebase fetch items

im building an react native with redux and Firebase Realtime Database, and I'm concerned about where to subscribe to fetch my items on a screen.
Im using useEffect to dispatch the subscription to firebase db:
useEffect(() => {
dispatch(userActions.fetchPets());
}, []);
and inside the action
export const fetchPets = () => {
return async dispatch => {
const user = await firebase.auth().currentUser;
firebase
.database()
.ref(`pets/${user.uid}`)
.on("child_added", snapshot => {
const pet = snapshot.val() || null;
dispatch({ type: ADD_PET, payload: pet });
});
};
};
My problem is when my screen re-render this action executes again filling with repeated data.
This is my reducer:
case ADD_PET:
return {
...state,
pets: [...state.pets, action.payload]
};
My question
Should I filter my state with key to delete repeated?
Should I put my subscription in another place? like a middleware or something? there is a pattern for this?
PS: "Sorry by my English"
the pattern you are using is fine.
If inside payload you have an array with new elements you have, your approach works fine. But, im assuming you are getting the same elements, just with any updated property. So, for example, if you have your pets store like this:
pets: [{id: 1, name: 'whatever'}], and your payload is : [{id: 1, name: 'whatever2'}], now you have both concatenated in your store, what is bad, because is the same object, updated.
So, if you will have the full list updated in the request, i would just change your reducer to this:
const initialState = { pets: [] };
case ADD_PET:
return {
...state,
pets: action.payload
};
So everytime you make the api request, you will have the updated list of elements.
Another case is if you get in the request only the updated, and you will have to filter your object based on ids, and then just replace the updated ones. But i dont think it is your case.

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.

( React-Native ) Detecting when an action dispatched a result or payload in component level

I am trying to load a list of data from an API call by an action defined like this:
export const searchForTrips = (searchParams) => {
let url = 'http://..............com/ferry/public/api/trip/search';
url = bindParamsToUrl(url, searchParams);
return (dispatch) => {
axios.get(url)
.then((response) => {
dispatch({ type: AVAILABLE_TRIPS_FETCH_SUCCESS, payload: response.data });
});
};
};
I am receiving the dispatch by a reducer and passing the payloads to component level props. When I make the action call in the ComponentDidMount method, the action returns null data. How to detect if the action is actually dispatched the payload or not from component?
I have solved the problem by using componentWillReceiveProps to receive the props when the props update is occurred and rendering the view according to the props.