I would like to dispatch a change action in a FormSection.
Currently I'm doing this in myForm.jsx:
<FormSection name="content">
<MyFormSection sectionName="content" formName="PanelForm"/>
</FormSection>
following this in MyFormSection.jsx
import { change } from 'redux-form';
// my component...
const mapDispatchToProps = (dispatch, props) => ({
change: (name, value) => dispatch(change(props.formName, props.sectionName+"."+name, value)),
})
I don't like to pass down the form both the name of the form and the section.
Would anybody have a better way of doing this?
If you are inside a Redux-Form Input component you can use this.props.input.onChange(value)
but if you are just wanting to create a function that when executed changes that value of a form which is not used in a Redux-Form Field Component, I think you are doing it the correct way. Here is another example I did just so you can see. This function was being passed down to a child form through props for one very specific way of changing values in my 'wizard' form.
const mapDispatchToProps = (dispatch) => {
return {
changeFormAction: (fieldName, value): void => {
dispatch({
type: '##redux-form/CHANGE',
meta: {
form: 'wizard',
field: `styles.[${fieldName}]`,
touch: false,
persistentSubmitErrors: false
},
payload: value
})
}
}
}
Related
I have a function that I use to make the API call and fetch the response. There is also another react file that renders the UI page. I'm able to store the response from the API using props and state. The issue I'm facing is that I'm able to prefill the input field with the correct data, but I'm unable to edit the input field. Also, I have to pass the input to another component.
Below is my code:
export const SelectorForm = ({ selectorDetail, selectorId, otherProp }) => {
return (
<>
<Row>
<input type="text" value={selectorDetail} onChange={updateSelectorIdAction} />
</Row>
</>
);
};
export const mapStateToProps = state => {
return {
selectorDetail: state.selectorDetail,
selectorId: selectorIdSelector(state),
};
};
export const mapDispatchToProps = {
updateSelectorIdAction: updateSelectorId,
};
export default SelectorForm;
export const updateSelectorId = (value) => ({
type: UPDATE_SELECTOR_ID,
payload: value,
});
What I need is to display the selectorDetail in the input field, take user input in selectorId and pass selectorId to another component (this part is done).
How should I change my onChange to do this?
I have problem when trying to send data through the function action in redux,
my code is below
import React from 'react'
import {connect} from 'react-redux'
import {RetrieveCompany} from '../../folder/action/my.actions'
interface Icampaing{
campaing: my_data
}
// campaing IS WORKING WELL, GET ME ALL MY DATA
const Personal: React.FC<Icampaing> = ({campaing}, props: nay) => {
React.useEffect(()=>{
let pf_id: any = campaing.profile ? campaing.profile.id : 0
let pc_id: any = campaing.profile_ca
// THE PROBLEM IS HERE SHOW ME THE ERROR
// TypeError: props.RetrieveCompany is not a function
props.RetrieveCompany(pf_id, pc_id)
},[campaing])
return(<>
{campaing.all_data} // HERE WHEN LOAD DATA campaing WORKING WELL
</>)
}
const mapStateToProps = (state: any) =>({
campaing: state.campaing
})
const mapActionToProps = {
RetrieveCompany
}
export default connect(mapStateToProps, mapActionToProps)(Personal)
please help me, I think forget something.
best words, and happy new year.....!
You should use mapDispatchToProps instead of mapActionToProps
const mapDispatchToProps = (dispatch) => ({
RetrieveCompany: () => dispatch(RetrieveCompany())
// Important: this could be just
// RetrieveCompany depends on how you define your action.
// For naming, I would use camelCase
});
Because what you need to do here is to dispatch an action so that the store will update its state. Then you would read the data returned by mapStateToProps.
I think RetrieveCompany is not among props in deed. Try to spread the rest of the props if you do not want to explicitly name it:
interface Icampaing {
campaing: my_data
[propName: string]: any
}
const Personal: React.FC<Icampaing> = ({ campaing, ...props }) => {
...
or simply add it explicitly since you use it in the component anyways:
interface Icampaing {
campaing: my_data
RetrieveCompany: (pf_id: number, pc_id: number) => void
}
const Personal: React.FC<Icampaing> = ({ campaing, RetrieveCompany }) => {
I have the following app in react and redux start kid
in a component, I am using a series of selector that are related to the same store Items :
const mapStateToProps = (state: RootState) => ({
itemsLoading: ItemsSelectors.getItemsIsLoading(state),
items: ItemsSelectors.getCurrentItemList(state),
fields: ItemsSelectors.getCurrentItemFields(state),
columns: ItemsSelectors.getCurrentItemColumns(state)
})
When the store values changes, I would like to update my component state, by doing some calculation with the data.
I am using the following function
UNSAFE_componentWillUpdate(nextProps) {
const displaybleTable = this.getDisplaybleTable(nextProps);
this.setState({
items : displaybleTable.items,
columns : displaybleTable.columns
})
}
So everytime the store change, I get updated, and I update the component state.
The problem is, since I update the component state, I am looping in this function.
Also, I believe it looks a bit wierd.
IS there a way to know when the store value has updates in the component, so thatr component can do some personal data manipulation ?
Which version of react do you use?
If I understood you correctly and assuming react version 16.8+, you can achiev this by using the useEffect() hook. I assume your component is connected to the store using connect() from 'react-redux'. Then it could look like this:
const MyComponent = (props) => {
useEffect(() => {
const displaybleTable = this.getDisplaybleTable(/* arguments */);
this.setState({
items : displaybleTable.items,
columns : displaybleTable.columns
})
}, [props.items])
const getDisplayableTable = (/* args: any */) => {
return ...
}
...
}
export const MyConnectedComponent = connect(
(state: RootState) => ({
itemsLoading: ItemsSelectors.getItemsIsLoading(state),
items: ItemsSelectors.getCurrentItemList(state),
fields: ItemsSelectors.getCurrentItemFields(state),
columns: ItemsSelectors.getCurrentItemColumns(state)
}),
{
// dispatchProps ...
},
(stateProps: any, dispatchProps: any, ownProps: any) => ({
itemsLoading: stateProps.itemsLoading,
items: stateProps.items,
fields: stateProps.fields,
columns: stateProps.columns
})
)(MyComponent)
The second parameter of useEffect defines when useEffect() calls the first parameter, which is a function. So each time 'items' is updated in the store, the update will trigger useEffect which will run the code and sets the state of your component.
EDIT:
ComponentWillUpdate(nextProps) will not be called if some values in your store changes. ComponentWillUpdate only gets called if the props you pass to your component has changed:
export const SomeOtherComponent = (props: any) => {
return (
<MyComponent prop1={val1} prop2={val2} />
)
}
If val1 and val2 changes this would call ComponentWillUpdate of MyComponent (as far as I know, but I'm not sure).
I am using react/redux to build my app. There are multiple buttons and the app needs to keep track of states of whether these buttons were clicked. These buttons are all using the same reducer logic, which is to alternate the state from true to false whenever the button is clicked. The only differentiator is the name of the state. Is there a way to reduce my code by defining a "global" reducer that can be applied to multiple states? Please refer to this image for an example
Thank you!
This is how I would do it. Instead of dispatching unique action for every button type like MENU_BTN, SEARCH_BTN, etc.. I would dispatch { type: 'TOGGLE_BUTTON', payload: 'menu' } and in reducer
case TOGGLE_BUTTON:
return {
[payload]: !state[payload]
...state
}
You can then toggle buttons like this
const toggleButton = key => ({
type: 'TOGGLE_BUTTON',
payload: key
})
dispatch(toggleButton('menu'))
dispatch(toggleButton('search'))
That way you can keep track of as many buttons as you want. Your state will then look something like this
...
buttons: {
menu: true,
search: false
}
...
and you can easily write selector for every button
// Component displaying state of menu button //
let MyComponent = ({ menuButtonState }) => (
<div>
Menu button is {menuButtonState ? 'on' : 'off'}
</div>
)
// Helper function for creating selectors //
const createGetIsButtonToggledSelector = key => state => !!state.buttons[key]
// Selector getting state of a menu button from store //
const getIsMenuButtonToggled = createGetIsButtonToggledSelector('menu')
// Connecting MyComponent to menu button state //
MyComponent = connect(state => ({
menuButtonState: getIsMenuButtonToggled(state)
})(MyComponent)
You can create a generic higher-order reducer that accepts both a given reducer function and a name or identifier.
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
function createNamedWrapperReducer(reducerFunction, reducerName) {
return (state, action) => {
const { name } = action
const isInitializationCall = state === undefined
if (name !== reducerName && !isInitializationCall) return state
return reducerFunction(state, action)
}
}
const rootReducer = combineReducers({
counterA: createNamedWrapperReducer(counter, 'A'),
counterB: createNamedWrapperReducer(counter, 'B'),
counterC: createNamedWrapperReducer(counter, 'C')
})
The above createdNamedWrapperReducer() method should work out of the box.
See Redux's recipes for Reusing Reducer Logic for a more detailed explanation or more examples.
First, here's my HOC:
export default function connectField({
nameProp = 'name',
valueProp = 'value',
dispatchProp = 'dispatch'
}: ConnectOptions) {
return compose(
getContext(contextTypes),
connect((state, ownProps) => {
const path = [namespace,...getPath(ownProps),...toPath(ownProps[nameProp])];
const value = getOr('', path, state);
return {
[valueProp]: value
};
}, (dispatch,ownProps) => { // <----------- mapDispatchToProps
const path = [...getPath(ownProps),...toPath(ownProps[nameProp])];
return {
[dispatchProp]: value => dispatch({type: ActionTypes.Change, payload: {path, value}})
};
}, (stateProps, dispatchProps, {[FIELD_PATH]: _, ...ownProps}) => {
return {...stateProps, ...dispatchProps, ...ownProps};
}, {
areMergedPropsEqual: (a,b) => {
let eq = shallowEqual(a,b);
console.log('areMergedPropsEqual',a,b,eq);
return eq;
},
}),
withContext(contextTypes, props => {
return {[FIELD_PATH]: [...getPath(props), props[nameProp]]};
}),
);
}
In the middle there is my mapDispatchToProps. That's causing areMergedPropsEqual to return false every time because it's creating a new action creator every time.
I can't figure out how to memoize this bit:
value => dispatch({type: ActionTypes.Change, payload: {path, value}})
Such that I get back the same function instance every time.
There's some notes in the docs about "per-instance memoization" which is what I want, but I can't quite make heads or tails of what I'm supposed to do here.
To be clear, I know how to memoize a function. However, I don't want a use a big cache with infinite history. It's unnecessary memory consumption. I just need a cache size of 1 like how reselect does it. The problem is that I can't create the "selector" directly inside connectField because that still creates a single shared instance -- i.e., all "connected fields" will share the same cache and they'll overwrite each other, negating the benefit. It has to be per component instance. This is specific to React-Redux's connect method. There's a syntax for it so that you can create your selector at the right spot, and it will only get ran once per instance. I'm just having trouble deciphering the API -- do they expect a function that returns a function that returns an object? Or an object with propnames as keys and functions as values? What does that function return? i.e., the docs aren't clear about all the different variations that are accepted for the mapDispatchToProps option.
If you already have lodash, you have a memoize function that allow to transform any function into a memoized function. This memoized function will calculate the return value for a given parameter and will then always return this same return value each you supply the same parameter.
You can use it like this for example :
import { memoize } from 'lodash'
const makeChangeDispatcher = memoize( (dispatch, path) =>
value => dispatch({type: ActionTypes.Change, payload: {path, value}})
)
...
(dispatch,ownProps) => { // <----------- mapDispatchToProps
const path = [...getPath(ownProps),...toPath(ownProps[nameProp])];
return {
[dispatchProp]: makeChangeDispatcher(dispatch, path)
};
}
...
You can see more infos on the lodash documentation of memoize