I'm trying to change the zoom level by passing a level of 13 into my action creator.
But get this error:
Error: Given action "ZOOM_SELECTED", reducer "zoom" returned undefined. To ignore an action, you must explicitly return the previous state. If you want this reducer to hold no value, you can return null instead of undefined.
Do you know how to change the current state passing a new zoom level into my action creator?
render() {
console.log(this.props.zoom)
return (
<div>
<Map className='map' center={this.props.mapCenter} zoom={this.props.zoom}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={this.props.mapCenter}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</Map> <button onClick={() => this.props.selectZoom((13))} >Ă„ndra zoom</button>
</div >
)
}
};
const mapStateToProps = (state) => {
return {
zoom: state.zoom,
mapCenter: state.mapCenterPosition
}
};
const mapDispatchToProps = dispatch => {
return {
selectZoom: () => dispatch(selectZoom())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MapComponent); ```
Action Creator
export const selectZoom = (zoom) => {
console.log('action',zoom)
return {
type: 'ZOOM_SELECTED',
payload: zoom
};
};
Reducer
const zoomReducer = (state = 8, action) => {
if(action.type === 'ZOOM_SELECTED') {
return action.payload;
} else {
return state;
}
};
The problem is in the mapDispatchToProps - you are not passing the zoom to the action in the callback. You need to do this:
const mapDispatchToProps = dispatch => {
return {
selectZoom: (zoom) => dispatch(selectZoom(zoom))
}
}
Without it, no argument gets passed and the payload has value of undefined. Since you are returning payload directly in the reducer, it appears as if it is not returning anything.
Related
I created an app which should simulate the buying of products. So, when i will click on the BUY button, the number of fruits should decrement.
I suppose that the problem is in my reducer, but i can' t understand where.
/////
const Fruits = (props) => {
console.log(props);
return (
<div>
<h1>Number of fruits: {props.numFruits} </h1>
<button onClick={buyFruitsAction}>BUY</button>
</div>
);
};
const mapStateToProps=(state)=> {
return {
numFruits: state.numFruits
}
};
const mapDispatchToProps=(dispatch)=> {
return {
buyFruitsAction: ()=> dispatch(buyFruitsAction())
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Fruits)
//// Reducer
import {BUY_FRUITS} from "../types";
const initialState = {
numFruits : 10
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case BUY_FRUITS : return {
...state,
numFruits: state.numFruits - 1
};
default: return state;
}
};
export default reducer;
Where is the issue of my code?
This line is the problem:
<button onClick={buyFruitsAction}>BUY</button>
There is no variable buyFruitsAction, it is a property. So that should read props.buyFruitsAction.
Your reducer is fine, can you post your action creator?
buyFruitsAction is undefined in your component do you meant props.buyFruitsAction?
This question may be silly, but for some reason I cannot find what I want to. I have a component which has two inputs, if any one of the input changes, I have to dispatch an action to saga to find some info from backend, so I have send both these values. When values are changed in one of the components I dispatch with new value and set it in redux store, for sending request to backend I need the other input value which is already set in redux store, how should I do this?
Below is the component and usecase, I want both duration and date values in change functions.
export class Options extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
return (
<div>
<SelectField
name={"duration"}
value={this.props.duration}
onChange={this.props.durationChanged}
>
<MenuItem key={1} value={1} primaryText={"option1"} />
<MenuItem key={2} value={2} primaryText={"option2"} />
<MenuItem key={3} value={3} primaryText={"option3"} />
<MenuItem key={4} value={4} primaryText={"option4"} />
</SelectField>
<DatePicker
value={this.props.date}
onChange={this.props.dateChanged}
/>
</div>
);
}
}
const mapStateToProps = createStructuredSelector({
date: makeSelectDate(),
duration: makeSelectDuration(),
});
function mapDispatchToProps(dispatch, a, b) {
return {
dispatch,
dateChanged: (any, date) => {
dispatch(changeDate(date));
//I want other duration value to dispatch an action to backend
},
durationChanged: (event, index, value) => {
dispatch(changeDuration(value));
//I want other date value to dispatch an action to backend
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Options);
I think, you need do:
1) Set only one prop "onChange" as function. And add props to define values on init. It like initValueDuration.
2) Set state with 3 fields
3) Add method onChange(state). This method will call: this.prop.onChange(this.state)
Say you have the two states duration, date in yourReducer. Then, you need two functions: durationChanged, dateChanged.
function durationChanged(newDuration) {
return (dispatch: Redux.Dispatch<any>, getState: () => RootState) => {
dispatch({
type: "DURATION_CHANGED",
payload: newDuration
});
const date = getState().yourReducer.date;
yourAsyncCallToTheBackEnd(newDuration, date);
...
}
}
function dateChanged(newDate) {
return (dispatch: Redux.Dispatch<any>, getState: () => RootState) => {
dispatch({
type: "DATE_CHANGED",
payload: newDate
});
const duration = getState().yourReducer.duration;
yourAsyncCallToTheBackEnd(duration, newDate);
...
}
}
In your mapDispatchToProps, you add
function mapDispatchToProps(dispatch: Redux.Dispatch<any>, ownProps: any): any{
return {
durationChanged: (newValue) => durationChanged(newValue),
dateChanged: (newValue) => dateChanged(newValue)
};
}
In yourReducer, you check the type of the action and change the corresponding state.
In your onChange handler of date input, you do this.props.dateChanged(event.target.value)
Checkout the redux-thunk
I'm loading it like that:
const store = createStore(
AppReducer,
composeEnhancers(applyMiddleware(createDebounce(), thunkMiddleware,
websocketMiddleware)),
);
I've got the following action:
export const toggleCardLayersMenu = (index, value) => ({
type: "CARD:TOGGLE_LAYERS_MENU",
index: index,
value: value,
});
Which I dispatch something to open a menu and something to close it.
Now, the chrome extension display the action with a small delay if the action is used to open the menu, and won't display the action at all if it's used to close the menu.
It occurs only with that specific action. What are the possible causes, and what can be done?
Edit:
This is where I dispatch the action:
...
<IconButton
onClick={ e => dispatches.toggleCardLayersMenu(e.currentTarget) }
><LayersIcon />
</IconButton>
<Menu
anchorEl={ card.menuBtnElem }
open={ card.isMenuOpen }
onRequestClose={ () => dispatches.toggleCardLayersMenu(null) }
style={ styles.menu }
>
...
This is the dispatchToProps():
const mapDispatchToProps = (dispatch, ownProps) => ({ dispatches: {
updateCardLayers(value) {
dispatch(displayActions.updateCardLayers(ownProps.index, value));
},
toggleCardLayersMenu(value) {
dispatch(displayActions.toggleCardLayersMenu(ownProps.index, value));
},
...
I did the basic redux todolist tutorial and it worked but I wanted to get to know the code by making a small change.
I changed:
actions/index.js
let nextTodoId = 0
export const addTodo = (text) => {
return {
type: 'ADD_TODO',
id: nextTodoId++,
text
}
}
to this:
let nextTodoId = 0
export const addTodo = (text) => {
return {
type: 'ADD_TODO',
payload: {
id: nextTodoId++,
text: text
}
}
}
And I got the adding a todo working with that but a strange side effect has occurred in the toggleTodo - There are no console errors but clicking a todo list item is supposed to toggle it between being completed (visually has a strike through the text) and being not completed. Clicking a list item now has no effect.
I'm struggling to pass this reducer an action which has a defined id.
reducers/todos.js:
This is the code which calls the toggleTodo(id) reducer (look for the arrow pointing and saying "HERE"):
containers/visibleTodoList.js:
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id)) <-------------HERE
}
}
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
export default VisibleTodoList
So the id is coming from onTodoClick.
components/TodoList.js:
So I pause it there and see the value of onTodoClick():
Where in the original code was it setting onTodoClick.id so I can repeat it again to get the onTodoClick.id to be defined (thus hopefully causing the clicking a todo item to toggle successfully).
You are looking for state.id !== action.id but you pass it as action.payload.id do the following:
case 'TOGGLE_TODO':
if (state.id !== action.payload.id) {
return state
}
In the example below, I am using mapDispatchToProps to bind the onSubmit event to the save() Action Creator. This works - the save() Action Creator logs the message to console.
The issue is that it does not subsequently dispatch the 'TEST_SAVE' action - i.e. the reducer never receives an action of type 'TEST_SAVE'.
I have a vanilla redux form working as below, but I am new to redux-form and I wonder what I might be doing wrong?
const reducers = {
// ... your other reducers here ...
form: formReducer.plugin({
contact: (state, action) => {
switch(action.type) {
case 'TEST_SAVE':
return state;
default:
return state;
}
}
})
};
const reducer = combineReducers(reducers);
const store = createStore(reducer);
function mapDispatchToProps(dispatch) {
return {
onSubmit: (val) => dispatch(actionCreators.save(val)),
}
}
var actionCreators = {
save: (value) => {
console.log('SAVE action done!');
return {type: 'TEST_SAVE', value};
},
};
const MyContactForm = connect(
mapDispatchToProps
)(ContactForm)
<Provider store={store}>
<MyContactForm />
</Provider>
An excerpt of the ContactForm class:
class ContactForm extends Component {
render() {
const { error, handleSubmit, pristine, reset, submitting } = this.props;
return (
<form
onSubmit={handleSubmit}
>
....
)
}
}
ContactForm = reduxForm({
form: 'contact'
})(ContactForm);
const MyContactForm = connect(
mapDispatchToProps
)(ContactForm)
The first parameter to connect() is mapStateToProps, not mapDispatchToProps. Should be:
const MyContactForm = connect(
undefined, // <------------- mapStateToProps
mapDispatchToProps
)(ContactForm)
Never use break inside a reducer. Always return the state object.