I'm wondering if there is a pattern that allows you to use action creators inside of other action creators. The modifyMassProperty action creator lets you pass any number of actions which are then iterated over and dispatched accordingly. I would very much like to be able to use this method in the getOrbitalBurn action creator since it would be semantically more appealing than using the dispatch method made available by the thunk three times in a row. I'm confident I must either have missed something, or that I'm guilty of getting tangled up in some sort of anti pattern that I concocted during one of my lesser days.
export const modifyMassProperty = (
...massProperties: MassProperty[]
): ThunkAction<void, AppState, void, Action> => (
dispatch: Dispatch<ScenarioActionTypes>
) =>
massProperties.forEach(massProperty =>
dispatch({
type: MODIFY_MASS_PROPERTY,
payload: massProperty
})
);
export const getOrbitalBurn = (
payload: { primary: string; periapsis: number; apoapsis: number },
applyBurn = true
): ThunkAction<void, AppState, void, Action> => (
dispatch: Dispatch<ScenarioActionTypes>,
getState: any
) => {
const scenario = getState().scenario;
const primary = getObjFromArrByKeyValuePair(
scenario.masses,
'name',
payload.primary
);
const orbit = orbitalInsertion(primary, payload, scenario.g);
if (applyBurn) {
const [spacecraft] = scenario.masses;
dispatch({
type: MODIFY_MASS_PROPERTY,
payload: {
name: spacecraft.name,
key: 'vx',
value: orbit.x
}
});
dispatch({
type: MODIFY_MASS_PROPERTY,
payload: {
name: spacecraft.name,
key: 'vy',
value: orbit.y
}
});
dispatch({
type: MODIFY_MASS_PROPERTY,
payload: {
name: spacecraft.name,
key: 'vz',
value: orbit.z
}
});
}
dispatch({
type: MODIFY_SCENARIO_PROPERTY,
payload: {
key: 'orbitalInsertionV',
value: { x: orbit.x, y: orbit.y, z: orbit.z }
}
});
};
Related
Error: Objects are not valid as a React child (found: object with keys {_id, name}). If you meant to render a collection of children, use an array instead.
Tried to fix this for days and no result.
i have a model
import mongoose from 'mongoose'
const CategoriesSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
parent: {
type: mongoose.Types.ObjectId,
ref: 'categories'
},
},
{
timestamps: true
})
let Dataset = mongoose.models.categories || mongoose.model('categories', CategoriesSchema)
export default Dataset
and i have getCategories like this
[getCategories ]
const getCategories = async (req, res) => {
try {
const categories = await Categories.find().populate("parent", "name");
res.json({ categories });
}
catch (err)
{
return res.status(500).json({ err: err.message });
}
};
in my Globale state i have
export const DataContext = createContext()
export const DataProvider = ({children}) => {
const initialState = {
notify: {}, auth: {}, cart: [], modal: [], orders: [], users: [], categories: []
}
const [state, dispatch] = useReducer(reducers, initialState)
useEffect(() => {
getData('categories').then(res => {
if(res.err)
return dispatch({type: 'NOTIFY', payload: {error: res.err}})
dispatch({ type: 'ADD_CATEGORIES', payload: res.categories })
})
},[])
return(
<DataContext.Provider value={{state, dispatch}}>
{children}
</DataContext.Provider>
)
}
when i call categories throw:exception
when i change dispatch in Globale state like :
dispatch({ type: 'ADD_CATEGORIES', payload: [] })
i get no elements in array :
Firstly, I'm working with React Native. I'm getting a data from Firebase and want to write to store (by Redux) quickly. But It doesn't work. You can find my all of codes below:
Function:
async getTumData (uid) {
const {selectedGroupDetail, getSelectedGroupDetail} = this.props;
var yeniGrupDetayi = {};
await firebase.database().ref("/groups/"+uid).once('value').then(
function(snapshot){
yeniGrupDetayi = {...snapshot.val(), uid: uid};
}).catch(e => console.log(e.message));
console.log("FONKSIYON ICERISINDEKI ITEM ==>", yeniGrupDetayi);
this.props.getSelectedGroupDetail(yeniGrupDetayi);
console.log("ACTION'DAN GELEN ITEM ===>", selectedGroupDetail);
}
Action:
export const getSelectedGroupDetail = (yeniGrupDetayi) => {
return {
type: GET_SELECTED_GROUP_DETAIL,
payload: yeniGrupDetayi
}
};
Reducer:
case GET_SELECTED_GROUP_DETAIL:
return { ...state, selectedGroupDetail: action.payload}
Çıktı:
FONKSIYON ICERISINDEKI ITEM ==> {admin: {…}, groupDescription: "Yaygın inancın tersine, Lorem Ipsum rastgele sözcü…erini incelediğinde kesin bir kaynağa ulaşmıştır.", groupName: "İnsan Kaynakları", groupProfilePic: "", members: {…}, …}
ACTION'DAN GELEN ITEM ===> {}
There is a FlatList in my page and I defined a button in renderItem of FlatList. When i click to this button, getTumData() function is working.
When i click to this button first time, selectedGroupDetail is null. Second time, it shows previous data.
How can i write a data to Store quickly and fast?
Thanks,
The thing is:
- You're using both async/await, and then/catch in your code.
- you're calling getSelectedGroupDetail before your async code resolves.
Fast Solution
getTumData = (uid) => {
const {selectedGroupDetail, getSelectedGroupDetail} = this.props;
var yeniGrupDetayi = {};
firebase.database().ref("/groups/"+uid).once('value').then(
(snapshot) => {
yeniGrupDetayi = {...snapshot.val(), uid: uid};
this.props.getSelectedGroupDetail(yeniGrupDetayi);
}).catch(e => console.log(e.message));
};
Better Solution:
1st: use Redux-Thunk middleware.
2nd: Move your Async code into your action creator: I mean this
async getTumData (uid) {
const {selectedGroupDetail, getSelectedGroupDetail} = this.props;
var yeniGrupDetayi = {};
await firebase.database().ref("/groups/"+uid).once('value').then(
function(snapshot){
yeniGrupDetayi = {...snapshot.val(), uid: uid};
}).catch(e => console.log(e.message));
console.log("FONKSIYON ICERISINDEKI ITEM ==>", yeniGrupDetayi);
this.props.getSelectedGroupDetail(yeniGrupDetayi);
console.log("ACTION'DAN GELEN ITEM ===>", selectedGroupDetail);
}
3rd: Your reducer should have another piece of data as an indicator for the time-gap before your selectedGroupDetail resolves:
// reducer initial state:
const INITIAL_STATE = { error: '', loading: false, selectedGroupDetail: null }
4th: Inside your action creator, you should dispatch 3 actions:
ACTION_NAME_START // This should should only set loading to true in your reducer.
ACTION_NAME_SUCCESS // set loading to false, and selectedGroupDetail to the new collection retured
ACTION_NAME_FAIL // in case op failed set error
5th: Your React component, should display a loading indicator (spinner or somthing), and maybe disable FlatList button during the loading state.
// Action creator
export const myAction = () => (dispatch) => {
dispatch({ type: ACTION_NAME_START });
firebase.database().ref("/groups/"+uid).once('value').then(
function(snapshot){
yeniGrupDetayi = {...snapshot.val(), uid: uid};
dispatch({ type: ACTION_NAME_SUCCESS, payload: yeniGrupDetayi });
}).catch(e => {
dispatch({ type: ACTION_NAME_FAIL, payload: e.message });
});
};
// Reducer
const INITIAL_STATE = {
loading: false,
error: '',
data: null,
};
export default (state = INITIAL_STATE, { type, payload }) => {
switch (type) {
case ACTION_NAME_START:
return {
...state,
error: '',
loading: true,
data: null,
};
case ACTION_NAME_SUCCESS:
return {
...state,
error: '',
loading: false,
data: payload,
};
case ACTION_NAME_FAIL:
return {
...state,
error: payload,
loading: false,
data: null,
};
default:
return state;
}
};
I wanted to dispatch an action from another action but not able to do so. When I try to do so it not able to found getAllUser method.
Below is my action class.
export const myActions = {
getAllUser() {
return (dispatch) => {
makeApiCall()
.then((response) => {
dispatch({
type: USER_SUCCESS,
payload: response,
});
})
.catch((error) => {
dispatch({
type: USER_FAILURE,
payload: error,
});
});
};
},
addUser(user) {
return (dispatch) => {
makeApiCall(user)
.then((response) => {
/*
Need help here :
wants to call above getAllUser()
.then(() =>
dispatch({
type: ADD_SUCCESS,
payload: response,
});
)
*/
};
},
};
I have tried various approaches like,
myActions.getAllUser()
.then((response) =>
dispatch({
type: ADD_SUCCESS,
payload: response,
});
);
and trying do dispatch directly,
const self = this;
dispatch(self.getAllUser());
dispatch({
type: ADD_SUCCESS,
payload: response,
});
One more way around this is after addUser success, update the reducer and than from UI call getAccount again to refresh the results, but just curious to know on how can I achieve this using multiple dispatch.
You can export the functions individually instead of wrapping it under the same object:
export const getAllUser = () => dispatch => { ... }
export const addUser = () => dispatch => {
...
dispatch(getAllUser());
}
You can still import them all if desired:
import * as myActions from '...';
Or you can declare getAllUser first then add to myActions, but the above solution is much cleaner.
const getAllUser = ...
const myActions = {
getAllUser,
addUser = ... { dispatch(getAllUser()) }
}
Using Flowtype together with Redux, I have a type like this:
export type MapState = {
addresses: Address[],
selected: Array<number>
}
and an action creator:
export const setParams = (params: any): Action => {
return { type: actionTypes.SET_PARAMS, payload: { params };
}
In the reducer, I merge the params into the state:
export default (state: MapState = initialState, action: SetParamsAction) => {
switch (action.type) {
case actionTypes.SET_PARAMS: {
return {
...state,
...action.payload.params
}
[...]
I'm looking for a possibility to tell Flowtype to accept params in the action creator, if it is an object consisting only of properties of MapState, so that I can get rid of the any in setParams. Any idea?
You can just add a exact PossibleParams Object type like so:
type PossibleParams = {|
addresses?: Address[],
selected?: number[],
|};
export const setParams = (params: PossibleParams): Action => ({
type: actionTypes.SET_PARAMS,
payload: {
params,
},
});
You can check all the possibilities on flow.org/try 🙂
In my react/redux/thunk application I use actions like:
function catsRequested() {
return {
type: CATS_REQUESTED,
payload: {},
};
}
function catsReceived(landings) {
return {
type: CATS_RECEIVED,
payload: landings,
};
}
function catsFailed(error) {
return {
type: CATS_FAILED,
payload: { error },
};
}
export const fetchCats = () => ((dispatch, getState) => {
dispatch(catsRequested());
return catsAPI.loadCats()
.then((cats) => {
dispatch(catsReceived(cats));
}, (e) => {
dispatch(catsFailed(e.message));
});
});
To deal with some data (simplified). Everything works but i have a lot of code for every data entity (and constants too).
I mean same functions for dogs, tigers, birds etc...
I see there are similar requested/received/failed action/constant for every entity.
What is right way to minify code in terms of redux-thunk?
You can keep your code DRY by creating a types and a thunk creators:
Type:
const createTypes = (type) => ({
request: `${type}_REQUESTED`,
received: `${type}_RECEIVED`,
failed: `${type}_FAILED`,
});
Thunk:
const thunkCreator = (apiCall, callTypes) => ((dispatch, getState) => {
dispatch({ type: callTypes.request });
return apiCall
.then((payload) => {
dispatch({ type: callTypes.received, payload }));
}, (e) => {
dispatch({ type: callTypes.failed, payload: e.message }));
});
});
Now you can create a fetch method with 2 lines of code:
export const fetchCatsTypes = createTypes('CATS'); // create and export the constants
export const fetchCats = (catsAPI.loadCats, fetchCatsTypes); // create and export the thunk
export const fetchDogsTypes = createTypes('DOGS'); // create and export the constants
export const fetchDogs = (dogsAPI.loadDogs, fetchDogsTypes ); // create and export the thunk
Note: you'll also use the types constant (fetchDogsTypes) in the reducers.