Flatten object properties in Flow - flowtype

I have a seemingly simple question about Flow.
Given the following objects:
const input: InputObject = {
key1: {
prop1: 'value1',
},
key2: {
prop2: 'value2',
prop3: false,
},
};
const output: OutputObject = {
prop1: 'value1',
prop2: 'value2',
prop3: false,
};
How can I get the type of OutputObject, based on InputObject?
type OutputObject = ??magic?? InputObject ??magic??;
Context
I want to type a React HOC component which will inject some properties based on a configuration object, like:
type Props = {
prop1: string,
prop2: string,
prop3: boolean,
};
class Component extends React.Component<Props> { /* */ }
Hoc({
key1: () => ({
prop1: 'value1',
}),
key2: () => ({
prop2: 'value2',
prop3: false,
}),
})(Component);
I already know how to correctly type the HOC to get it type-safe and how to get InputObject from the configuration object passed to the HOC, but I'm stuck at how to "flatten" the InputObject type to OutputObject type, so that <Component /> will be happy because it will be getting all the required properties from the HOC :)
Even more important! I want the following to be a type error:
const HocComponent = Hoc({
key1: () => ({
prop1: 'value1',
}),
key2: () => ({
prop2: 'value2',
}),
})(Component);
<HocComponent /> // Error! prop3 is required
<HocComponent prop3={true} /> // Happy again
Thank you!

Related

Next js Redux, Objects are not valid as a React child

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 :

Redux action how to update only one objects state

I have an exercise app where a user inputs a 'name' and 'weight'. Then they are allowed to update the 'weight' of a specific 'name'. I am having trouble figuring out how to let them do this, and the closest I have gotten so far is the 'weight' of every 'name' getting updating to the exact same number. For example, if I have:
[
{
name: 'bench',
weight: 100
},
name: 'squat',
weight: 200
},
]
and then the user tried to update just the weight of bench to 300, what happens is both bench and squat get updated to 300. I want just the weight of bench to get updated though.
Here is my code so far. First, my actions:
export const addMovement = (formValues) => {
return {
type: constants.ADD_MOVEMENT,
payload: formValues,
}
};
export const updateMovement = (formValues) => {
return {
type: constants.UPDATE_MOVEMENT,
payload: formValues,
}
};
My reducers:
const initialState = [];
const movementReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_MOVEMENT:
return [ ...state, action.payload ];
case UPDATE_MOVEMENT:
return [
...state.map(item => Object.assign({}, item, { movementWeight: action.payload.movementWeight }))
];
default:
return state;
}
};
If more code is needed I will provide it, just ask. Any help or tips would be appreciated.
If you find object for update by "name" field so you can try with:
case UPDATE_MOVEMENT:
return [
...state.map(item => item.name === action.payload ? action.payload : item )
];

redux : Select/get nested State by dynamic keys

Here my redux state , the state has dynamic nested object name
const search = {
client :
{ result: [],
selected: null,
isLoading: false,
isSuccess: false,},
[dynamicKey] :
{ result: [],
selected: null,
isLoading: false,
isSuccess: false,},
[dynamicKey2] :
{ result: [],
selected: null,
isLoading: false,
isSuccess: false,}
};
I'm trying to get nested object by dynamic key , here is my selector code :
import { createSelector } from "reselect";
export const searchState = (state) => state.search;
export const selectSearch = (keyRef) =>
createSelector([searchState], (search) => search[keyRef]);
You forgot to ask the question but your code looks fine as it is. In the component you can use useMemo to not needlessly create the selector:
//renamed the selector to create...
export const createSelectSearch = (keyRef) =>
createSelector([searchState], (search) => search[keyRef]);
//compnent example
const MyComponent = ({keyRef}) => {
const selectSearch = React.useMemo(
()=>createSelector(keyRef),//create the selector when keyRef changes
[keyRef]
);
const result = useSelector(selectSearch)
return <jsx />
}
Some more information about this pattern can be found here

Use action creator to dispatch action in another action creator

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 }
}
});
};

Allow partial type

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 🙂

Resources