Need help for my first steps with React-Redux - redux

I am struggeling on getting my first steps with Redux. All of these "Todo-App" Tutorials are nice, the "Increment the button" tutorials as well. I thought of getting my own example to teach myself the logic of Redux, but something doesnt work. At the moment, I am not sure where the state comes from, so I tried a lot of different variations to have Redux "started" without getting initialization errors, and I found a working solution! First, I just setted up the state in the reducer, but the button-describtion didnt appear. Then, I setted up the state in the store additionally, and at least the button has the decribtion test123 and the console.log worked. But how to get the state from the reducer (I checked the documentation and it was recommended to pass state by reducers, not by the store itself). At the moment, I get the following error:
Error: Objects are not valid as a React child (found: object with keys {0, 1, 2, 3}). If you meant to render a collection of children, use an array instead.
Here is my absolutely basic code which should help me understand the logic of redux:
Action type:
export const CLICK = 'CLICK'
Action Creator:
import { CLICK } from './types';
export function clicked() {
return({
type: CLICK,
payload: 'switch the describtion of the button'
})
}
the clickReducer:
import { CLICK } from '../actions/types';
const initialState = {
name: 'test'
}
export default function (state = initialState, action) {
console.log('click-test', action)
switch(action.type) {
case CLICK: {
return Object.assign({}, state)
}
default:
return state
}
}
the rootReducer:
import { combineReducers } from 'redux';
import clickReducer from './clickReducer';
export default combineReducers({
name: clickReducer
})
the store:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {
name: 'test123'
};
const middleWare = [thunk];
const store = createStore(rootReducer, initialState, applyMiddleware(...middleWare));
export default store;
and the button-component:
import React, { Component } from 'react'
import { connect } from 'react-redux';
import { clicked } from '../actions/clickAction';
class Button extends Component {
render() {
return (
<div>
<button onClick={this.props.clicked}>{this.props.name}</button>
</div>
)
}
}
const mapStateToProps = state => ({
name: state.name
});
export default connect(mapStateToProps, { clicked })(Button);
It would be very nice to get some help with this issue to be able to take further steps in Redux.
Thank you!

You don't need parentheses, do this instead:
import { CLICK } from './types';
export clicked = () => {
return {
type: CLICK,
payload: 'switch the describtion of the button'
}
}
Your "CLICK" type in the switch statement isn't updating the name, you're just returning the state. Do this, instead:
import { CLICK } from '../actions/types';
const initialState = {
name: 'test'
}
export default (state = initialState, action) => {
switch(action.type) {
case CLICK:
return {
...state,
name: action.payload
}
default:
return state
}
}
Your store has too much information, do this instead:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
export default store;
Call the object property:
import React, { Component } from 'react'
import { connect } from 'react-redux';
import { clicked } from '../actions/clickAction';
class Button extends Component {
render() {
return (
<div>
<button onClick={this.props.clicked}>{this.props.name.name}</button>
</div>
)
}
}
const mapStateToProps = state => ({
name: state.name
});
export default connect(mapStateToProps, { clicked })(Button);

this solution, regarding the reducer, still leads to the following error:
Error: Objects are not valid as a React child (found: object with keys {0, 1, 2, 3}). If you meant to render a collection of children, use an array instead.
in button (at button.js:9)
in div (at button.js:8)
in Button (created by ConnectFunction)
in ConnectFunction (at App.js:12)
in div (at App.js:11)
in Provider (at App.js:10)
in App (at src/index.js:6) react-dom.development.js:57
React 15
dispatchInteractiveEvent self-hosted:1029
I really cant imagine why it is like this, because my solution looks like okay and this is a very very primitive app to change the button description :(((

Related

Why this error is coming TypeError: Cannot read property 'type' of undefined

How to solve this error TypeError: Cannot read property 'type' of undefined.
I am very new to react-redux and redux, simply I am trying to do state management through redux.
For this I installed react-redux and redux npm packages. And I created a store, In store a have reducer.js file. Even I imported Provider and store in index.js. Help to resolve this issue.
In store folder I have reducer.js file
This is reducer.js file code
const initialState = {
age: 21
}
const reducer = (state = initialState, action) => {
const newState = {state};
if(action.type === 'AGE_UP') {
newState.age++
}
if(action.type === 'AGE_DOWN') {
newState.age--
}
return newState;
};
export default reducer
This is App.js
import React, { Component } from 'react';
import './App.css'
import { connect } from 'react-redux'
class App extends Component {
render() {
return (
<div className='App'>
<div>Age: <span>{this.props.age}</span></div>
<button onClick={this.props.onAgeUp}>Age UP</button>
<button onClick={this.props.onAgeDown}>Age Down</button>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
age:state.age
}
}
const mapDispatchToProps = (dispatch) => {
return {
onAgeUp: () => dispatch({type: 'AGE_UP'}),
onAgeDown: () => dispatch({type: 'AGE_DOWN'})
}
}
// export default connect()(App)
export default connect(mapStateToProps,mapDispatchToProps) (App)
This is index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reducer from './store/reducer';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import * as serviceWorker from './serviceWorker';
const store = createStore({}, reducer);
ReactDOM.render(<Provider projectStore={store}><App></App></Provider>, document.getElementById('root'));
serviceWorker.unregister();
I can see multiple issues in your code:
createStore takes reducer as the first argument, look at API documentation, try this:
const store = createStore(reducer);
Provider has prop store and not projectStore, look at API documentation, try this:
ReactDOM.render(<Provider store={store}><App></App></Provider>, document.getElementById('root'));
You probably mutate your state in a reducer, try this:
const reducer = (state = initialState, action) => {
// it is better to use switch instead of if in reducer, for sake of readability
switch (action.type) {
case 'AGE_UP':
// it is better to have return statement here, it is more robust to developer errors
// it is better to use object spread and return result here than creating `newState` variable, you will do less errors
return {...state, age: state.age + 1}
case 'AGE_DOWN':
return {...state, age: state.age - 1}
}
return state;
};

mapDispatchToProps function is undefined

I am trying to get redux working in my react-native app. Basically, I have a signIn action defined in my authActions.js file:
const signInAction = () => {
return {
type: 'signIn',
};
};
export { signInAction };
Then I have an authReducer defined as this in authReducer.js:
const initialState = {
isAuthenticated: false,
}
const authReducer = (state = initialState, action) => {
switch(action.type) {
case "signIn":
return Object.assign({}, state, {
isAuthenticated: true,
})
default: return state;
}
};
export default authReducer;
I combine that reducer in my rootReducer.js file
import { combineReducers } from 'redux';
import auth from 'app/src/redux/reducers/authReducer.js';
const rootReducer = combineReducers({
auth,
});
export default rootReducer;
and then created a store in reduxIndex.js:
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from 'app/src/redux/reducers/rootReducer.js';
let store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
export default store;
I wrapped my app in a <Provider> component, and that seems to be working fine (I can read from the state and see the value of isAuthenticated. However, when I try to dispatch an action using mapDispatchToProps in one of my views the function is undefined:
// More imports
// ...
import { connect } from 'react-redux';
import { signInAction } from 'app/src/redux/actions/authActions.js';
const mapStateToProps = (state) => {
return {};
}
const mapDispatchToProps = (dispatch) => {
return {
onSignIn: () => { dispatch(signInAction) },
};
}
class SignIn extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
}
}
onSignInPress() {
// ******* this is where the error occurrs ****
this.props.onSignIn();
}
render() {
const {navigation} = this.props;
return (
<View style={SignInStyles.container}>
<ScrollView>
<View>
<Button
large
title="SIGN IN"
backgroundColor={colors.primary}
onPress={this.onSignInPress}
/>
</View>
</ScrollView>
</View>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(SignIn);
I cant really see where I am going wrong, but im sure its a simple mistake somewhere. The specific error I get is :
"undefined is not an object. Evaluating this.props.onSignIn"
The onSignInPress callback isn't bound to any particular object, so when it gets called this is undefined.
The easy way to fix it is to use arrow syntax to make it always be bound. In your class definition:
onSignInPress = () => {
this.props.onSignIn();
}
Google found me this Medium article from Miron Machnicki which explains the differences and possible alternative syntaxes in pretty good detail.

TypeError: Object(...) is not a function (redux)

I am having a TypeError (TypeError: Object(...) is not a function) when I want to dispatch an action. I'm not using any middleware and don't know what I can do to solve it. I had this error already yesterday but somehow managed to solve it (i donk know how i did this)
This is the App.js:
import React from "react";
import { store } from "../store";
import { withdrawMoney} from "../actions";
const App = () => {
return (
<div className="app">
<div className="card">
<div className="card-header">
Welcome to your bank account
</div>
<div className="card-body">
<h1>Hello, {store.getState().name}!</h1>
<ul className="list-group">
<li className="list-group-item">
<h4>Your total amount:</h4>
{store.getState().balance}
</li>
</ul>
<button className="btn btn-primary card-link" data-amount="5000" onClick={dispatchBtnAction}>Withdraw $5,000</button>
<button className="btn btn-primary card-link" data-amount="10000" onClick={dispatchBtnAction}>Witdhraw $10,000</button>
</div>
</div>
</div>
);
}
function dispatchBtnAction(e) {
store.dispatch(withdrawMoney(e.target.dataset.amount));
}
export default App;
Here is the actioncreator:
function withdrawMoney(amount) {
return {
type: "ADD_TODO",
amount
}
}
If you need here is the reducer:
export default (state, action) => {
console.log(action);
return state
}
As you can see I'm a very new to redux but I'd like to know what mistake I make all the time when dispatching an action. Thanks
I believe the issue is that you aren't exporting the withdrawMoney function, so you aren't able to call it in the component that you're attempting to import into.
try:
export function withdrawMoney(amount) {
return {
type: "ADD_TODO",
amount
}
}
Another subtle mistake that will cause this error is what I tried to do, don't accidentally do this:
import React, { useSelector, useState ... } from 'react'
it should be:
import React, { useState } from 'react'
import { useSelector } from 'react-redux'
Try to install :
npm i react#next react-dom#next and run again
const mapStateToProps = state => ({
Bank: state.Bank,
});
function mapDispatchToProps(dispatch) {
return {
dispatch,
...bindActionCreators({ getBanks, addBank }, dispatch)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(BankComponent);
this worked like a Charm for me .
bit Late to party,
to me it was about casing.
export const addTodo = text => ({
type: ADD_TODO,
desc: text
});
export const removeTodo = id=> ({
type: REMOVE_TODO,
id: id
})
export const increaseCount = () => ({
type: INCREASE_COUNT
});
export const decreaseCount = () => ({
type: DECREASE_COUNT
})
when I renamed all those like
export const AddTodo = text => ({
type: ADD_TODO,
desc: text
});
export const RemoveTodo = id => ({
type: REMOVE_TODO,
id: id
})
export const IncreaseCount = () => ({
type: INCREASE_COUNT
});
export const DecreaseCount = () => ({
type: DECREASE_COUNT
})
it worked.
I spent hours debugging this, turns out I was doing this:
import React, { connect } from "react";
Instead of
import React from "react";
import { connect } from "react-redux";
It's weird the former didn't throw an error, but it will cause the problem!
First problem I see right off the bat is your reducer is not setup to do anything with the dispatched withdrawMoney action creator.
export default (state, action) => {
switch (action.type) {
case "ADD_TODO": {
return {
...state,
amount: action.amount,
};
}
default:
return state;
}
};
If this does not help, the code from your other files would be helpful.
It may not be the main issue, but it looks like you're not using the React-Redux library to work with the store. You can reference the store directly in your React components, but it's a bad practice. See my explanation for why you should be using React-Redux instead of writing "manual" store handling logic.
After updating react-redux to version 7.2.0+ I was receiving this error anytime I wrote:
const dispatch = useDispatch()
I stopped my application and re-ran it with npm start, and everything is working now.
I was using redux toolkit and for me the problem was an extra '}'
such that my 'reducers' object, in 'createSlice' function, already had a closing curly brace before my second reducer
and my 2nd reducer was actually outside the 'reducers' object,
making it not a reducer and hence not working even when you export or import it properly.
So the problem is not in your export or import, but actually where your function is defined.
This may be different for other users, but in my case this turned out to be the cause of this error.

REDUX: Why won't the store provide data to my component?

Newbie here trying to learn some Redux.
GOAL: to get a button to click and login/logout, updating the store as true/false status whichever way.
const store = createStore(myReducer)
Created my store, passing in my reducer.
This has a default state of logged out. And returns the opposite, whenever the button is clicked.
I know this action works through debugging.
function myReducer(state = { isLoggedIn: false }, action) {
switch (action.type) {
case 'TOGGLE':
return {
isLoggedIn: !state.isLoggedIn
}
default:
return state
}
}
The problem starts here - when i try to access the store.getState() data.
class Main extends React.Component {
render() {
return (
<div>
<h1>Login Status: { state.isLoggedIn }</h1>
<button onClick={this.props.login}>Login</button>
</div>
)
}
}
const render = () => {
ReactDOM.render(<Main status={store.getState().isLoggedIn} login={() => store.dispatch({ type: 'TOGGLE' })}/>, document.getElementById('root'));
}
store.subscribe(render);
render();
I've tried store.getState().isLoggedIn & store.getState() & this.props.status and then assigning the store.getState().isLoggedIn in the Main component - but nothing works.
Can anyone tell me where i'm going wrong?
You don't directly access the store using getState to find data. The Redux docs explain the process in-depth, but basically you'll connect each component to the Redux store using connect method of the react-redux package.
Here's an example of how this could work for your above component:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Main from '../components/Main'
class MainContainer extends Component {
render() {
return <Main {...this.props} />
}
}
const mapStateToProps = state => ({
isLoggedIn: state.isLoggedIn,
})
const mapDispatchToProps = dispatch => ({
login() {
dispatch({type: 'TOGGLE'})
},
})
MainContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(MainContainer)
export default MainContainer
You would then want to render the MainContainer in place of the Main component. The container will pass down isLoggedIn and login as props to Main when it renders it.

Redux action is not being fired

Redux action changePictogramsKeyword is not being fired.
This is the file where I define my action and reducer (redux/module/keyword.js):
export const CHANGE_PICTOGRAMS_KEYWORD = 'CHANGE_PICTOGRAMS_KEYWORD'
export function changePictogramsKeyword (keyword) {
return {
type: CHANGE_PICTOGRAMS_KEYWORD,
keyword
}
}
// Updates error message to notify about the failed fetches.
export default function pictogramsKeyword (state = '', action) {
switch (action.type) {
case CHANGE_PICTOGRAMS_KEYWORD:
return action.keyword
default:
return state
}
}
My root reducer:
import { combineReducers } from 'redux'
import { routerReducer as router } from 'react-router-redux'
import locale from './modules/locale'
import errorMessage from './modules/error'
import pictogramsKeyword from './modules/keyword'
export default combineReducers({
locale,
router,
pictogramsKeyword,
errorMessage
})
So with the devTools I can check that my initialState is as I expected from the rootReducer:
locale:"en"
router:{} 1 key
pictogramsKeyword:""
errorMessage:null
This is the code of the view where I connect to Redux Store. Component SearchBox is in charge of firing the action changePictogramsKeyword:
import React, {Component, PropTypes} from 'react'
import SearchBox from 'components/SearchBox.js'
import { connect } from 'react-redux'
import { changePictogramsKeyword } from 'redux/modules/keyword'
class SearchPictogramsView extends Component {
handleDismissClick (e) {
this.props.resetErrorMessage()
e.preventDefault()
}
render () {
const { children, inputValue } = this.props
return (
<div>
<SearchBox value={inputValue} onChange={changePictogramsKeyword} />
{children}
</div>
)
}
}
SearchPictogramsView.propTypes = {
inputValue: PropTypes.string.isRequired,
children: PropTypes.node
}
function mapStateToProps (state, ownProps) {
return {
errorMessage: state.errorMessage,
inputValue: state.pictogramsKeyword
}
}
export default connect(mapStateToProps, {
resetErrorMessage, changePictogramsKeyword
})(SearchPictogramsView)
This is the code of the SearchBox component. AutoComplete is a material-ui component. onUpdateInput method gets fired everytime I press a key, however changePictogramsKeyword is not being fired (i see nothing through the dev tools)
import React, {Component, PropTypes} from 'react'
import AutoComplete from 'material-ui/lib/auto-complete'
import RaisedButton from 'material-ui/lib/raised-button'
class SearchBox extends Component {
constructor (props) {
super(props)
this.handleUpdateInput = this.handleUpdateInput.bind(this)
}
handleUpdateInput = (t) => {
console.log(t)
this.props.onChange(t)
}
render () {
return (
<div>
<AutoComplete onUpdateInput={this.handleUpdateInput} searchText={this.props.value} />
</div>
)
}
}
SearchBox.propTypes = {
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired
}
export default SearchBox
Right now, your action only gets called, but not dispatched because you're not mapping the actions correctly in the connect() call. (see the official documentation for more information)
In your SearchPictogramsView, change the mapDispatchToProps function of the connect() call to return an object with the wrapped functions:
export default connect(mapStateToProps, (dispatch) => {
return {
resetErrorMessage: () => dispatch(resetErrorMessage()),
changePictogramsKeyword: () => dispatch(changePictogramsKeyword())
};
})(SearchPictogramsView)
You can clean it up by making mapDispatchToProps its own function too:
function mapDispatchToProps(dispatch) {
return {
resetErrorMessage: () => dispatch(resetErrorMessage()),
changePictogramsKeyword: () => dispatch(changePictogramsKeyword())
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SearchPictogramsView)
Let me know if that works!
It was really in the docs:
If an object is passed, each function inside it will be assumed to be
a Redux action creator. An object with the same function names, but
with every action creator wrapped into a dispatch call so they may be
invoked directly, will be merged into the component’s props
When I wrote:
<SearchBox value={inputValue} onChange={changePictogramsKeyword} />
Now is:
<SearchBox value={inputValue} onChange={this.props.changePictogramsKeyword} />
So I really call the dispatch of the action and not just the action!

Resources