Reformatting initialValues in redux-form - redux

I am loading the Redux form with initialValues being passed as props. However, I need to reformat the data so as to fit my form names .. so e.g if the data in initialValues passed as props is {name: 'John', age: '32'}, I want to convert it to {user: {name: 'John', age: '32'}}. How do you achieve it ? Do you write a reducer to accomplish this and if yes then how do you invoke it at the component load ?
Thanks.

You could just do it when you pass in the prop.
const userData = { name: 'John', age: 32 }
...
<MyForm initialValues={{ user: userData }}/>

If you need some bigger changes, you can also pass a function to initialValues:
const getInitialValues = (propValues) => {
const initialValues = {};
// your function to format the values here
return initialValues;
};
#reduxForm(
{
form: 'myForm',
fields: formFields,
},
state => ({
initialValues: getInitialValues(state.userData),
form: state.form
})
)

Related

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

How does one have multiple cases that run the same code with Redux Toolkit?

I'm working on converting some older Redux code to use the new Redux Toolkit. I've run into a problem where, in the old code, multiple case statements would trigger the same reducer logic. How does one do this with the new case reducer functions?
In the old code, REGISTER_FAIL, AUHT_ERROR, LOGIN_FAIL, LOGOUT all run the same code. Is it possible to have this same type scenario in the createSlice reducers object?
Old Code
case REGISTER_FAIL:
case AUTH_ERROR:
case LOGIN_FAIL:
case LOGOUT:
localStorage.removeItem('token');
return {
...state,
token: null,
isAuthenticated: false,
loading: false,
user: null,
};
default:
return state;
New Code
const authUserSlice = createSlice({
name: 'authUser',
initialState,
reducers: {
registerFail(state, action) {
return {
...state,
token: null,
isAuthenticated: false,
loading: false,
user: null,
};
},
registerSuccess
},
});
There's a couple different options.
First, you could write the case reducer functions separately, then pass them to createSlice multiple times to generate corresponding actions for each field name:
function resetState() {
Object.assign(state, {
token: null,
// etc
});
}
const authUserSlice = createSlice({
name: 'authUser',
initialState,
reducers: {
registerFailed: resetState,
logout: resetState,
// etc
}
});
The other option is to use the extraReducers field, and use builder.addMatcher() to handle multiple cases with the same reducer:
const authUserSlice = createSlice({
name: 'authUser',
initialState,
reducers: {
// omit
},
extraReducers: builder => {
builder.addMatcher(
// can pass multiple RTK action creators here
isAnyOf(registerFailed, loginFailed),
(state, action) => {
// reset state here
}
)
}
});
If you're still interoping with old code and have old-style action constants like const LOGIN_FAILED = "LOGIN_FAILED" and need to match those, you can write your own "matching" function that just does a string type comparison, like:
builder.addMatcher(
(action) => [LOGIN_FAILED, REGISTER_FAILED].includes(action.type),
(state, action) => {}
)

Nested object destructuring with redux and react

I'm following a tutorial in React Redux. I have created a store variable with Redux store which has two sub variables. One is expenses which is an array of objects and another is filters which is an object itself.
const store = createStore(
combineReducers({
expenses: expensesReducer,
filters: filtersReducer
})
);
When filled with dummy values the store would look like this:
const dummyState = {
expenses: [{
id: '10-AC-191',
title: 'January Rent',
note: 'This was the final payment for that address',
amount: 545.00,
createdAt: 0
}, {
id: '10-AK-155',
title: 'Breakfast',
amount: 545.00,
createdAt: 2000
}],
filters: {
text: 'rent',
sortBy: 'amount',
startDate: 700,
endDate: 360,
}
};
I'm currently writing a function to display resultant expenses array which looks like this.
const getVisibleExpenses = (expenses, {text, sortBy, startDate, endDate}) => {
return expenses.filter(({title, note, createdAt}) => {
const startDateMatch = typeof startDate !== 'number' || createdAt >= startDate;
const endDateMatch = typeof endDate !== 'number' || createdAt <= endDate;
const searchText = text.trim().toLowerCase();
const textMatch = typeof text !== 'string' || title.toLowerCase().includes(searchText)
|| note.toLowerCase().includes(searchText);
return startDateMatch && endDateMatch && textMatch;
}).sort((expense_a, expense_b) => {
if (sortBy === 'amount') return expense_a.amount - expense_b.amount;
else if (sortBy === 'date') return expense_a.createdAt - expense_b.createdAt;
});
};
This function takes store.expenses, store.filters as two inputs. So I wanted to pass in store object only and get the output.
Hence I tried to object destructure the input store itself instead of calling store below. But it returns an error.
const getVisibleExpenses = ({expenses, {text, sortBy, startDate, endDate}})
Is there any possible solutions?
Try this:
const getVisibleExpenses = ({expenses, filters: {text, sortBy, startDate, endDate}}) => {}

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

Rendering data in FlatList from firebase

I am using React Native 0.49. I have data fetched from firebase, list of users users/, each item in this list was set like this firebase.database().ref('users/' + userId).set(userInfo) userId is the uid of the currentUser.
Now I am fetching back (in actions - redux):
export function getPeople(){
return (dispatch) => {
dispatch(getPeopleRequest());
getData().then(result => {
dispatch(getPeopleSuccess(result.val()))
})
.catch(error => {
dispatch(getPeopleFailure(error))
});
}
}
const getData = () => {
const userRef = firebase.database().ref('users/').limitToFirst(20);
return userRef.once('value');
}
In component, I am trying to render the data in FlatList, but it's not rendering anything, I don't know what I'm doing wrong:
componentDidMount(){
this.props.getPeople();
}
_renderItem = ({ item }) => (
<View style={{flex: 1}}>
<Text>{item}</Text>
</View>
);
render(){
const { inProgress, people, error } = this.props.peopleData;
return (
<FlatList
data={people}
renderItem={this._renderItem}
/>
);
}
when console log people this is result:
{cpycwkz7exVBzmhaLEtHjMW66wn1: {…}, UbIITfUANmb63cYE2x7dZRZ0pfK2: {…}}
FlatList component expects its data prop to be an array. You are passing it as an Object. You can change it to an array of Objects. Then too in your _renderItem method the item will be an object and it can't be rendered straight away in <Text>, you have to extract a text value from the item object and than render it as: <Text>SOME_TEXT_NOT_AN_OBJECT</Text>
You can convert your people object to an array and pass it to the <FlatList like this:
render(){
const { inProgress, people, error } = this.props.peopleData;
let ArrayOfPeopleObject = Object.values(people)
return (
<FlatList
data={ArrayOfPeopleObject}
renderItem={this._renderItem}
/>
);
}
Now each item in the _renderItem method will be an object and you can extract value from any key and render it in the <Text>.
Flatlist data requires a key for each object in the array you can convert the firebase result like this:
Object.entries(peopleFromFirebase).map(item => ({...item[1], key: item[0]}));
So json from firebase like this:
{
cpycwkz7exVBzmhaLEtHjMW66wn1: {
name: 'wade owen watts',
phone:'+447...'
},
UbIITfUANmb63cYE2x7dZRZ0pfK2: {
name: 'Helen Harris',
phone:'+448...'
}
}
becomes:
[
{
key: 'cpycwkz7exVBzmhaLEtHjMW66wn1',
name: 'wade owen watts',
phone:'+447...'
},
{
key:'UbIITfUANmb63cYE2x7dZRZ0pfK2',
name: 'Helen Harris',
phone:'+448...'
}
]
Flat list except array of objects but the firebase return the data as map like {key: value} pair so you should transform this map to array , you can install lodash module and use _.values() function to do that

Resources