Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(CharacterList) - javascript

I am trying to test my React "supersquadapp" and getting the following error.
Uncaught Error: Could not find "store" in either the context or props of "Connect(CharacterList)". Either wrap the root component in a , or explicitly pass "store" as a prop to "Connect(CharacterList)".
characterlist.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
class CharacterList extends Component {
render() {
console.log('this.props', this.props);
return (
<div>
<h4>characters</h4>
</div>
)
}
}
function mapStateToProps(state) {
return {
characters: state.characters
}
}
export default connect(mapStateToProps, null)(CharacterList);
app.js
import React, {Component} from 'react';
import CharacterList from './CharacterList';
class App extends Component {
render() {
return (
<div>
<h2>SUPER SQUAD</h2>
<CharacterList />
</div>
)
}
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './Components/App';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducers from './reducers';
import { addCharacterById } from './actions';
const store = createStore(rootReducers);
console.log(store.getState());
store.subscribe(() => console.log('store',store.getState()))
store.dispatch(addCharacterById(3));
ReactDOM.render(
<Provider>
<App />
</Provider>
,document.getElementById('root')
)
character.js(in reducers folder)
import characters_json from '../Data/characters.json';
import { ADD_CHARACTER } from '../actions';
function characters(state = characters_json, action) {
switch(action.type) {
case ADD_CHARACTER:
let characters = state.filter(item => item.id !== action.id);
return characters;
default:
return state;
}
}
export default characters;
heroes.js(reducers folder)
import { ADD_CHARACTER } from '../actions';
import {createCharacter} from './helper';
function heroes(state = [], action) {
switch(action.type) {
case ADD_CHARACTER:
let heroes = [...state, createCharacter(action.id)];
return heroes;
default:
return state;
}
}
export default heroes;
helper.js(reducers folder)
import characters_json from '../Data/characters.json';
export function createCharacter(id) {
let character = characters_json.find(c => c.id === id);
return character;
}
index.js(reducers folder)
import { combineReducers } from 'redux';
import characters from './characters_reducer';
import heroes from './heroes_reducer';
const rootReducer = combineReducers({
characters,
heroes
})
export default rootReducer;
index.js(action folder)
export const ADD_CHARACTER = 'ADD_CHARACTER';
export function addCharacterById(id) {
const action = {
type:ADD_CHARACTER,
id
}
return action;
}
The above error occurred in the component:
in Connect(CharacterList) (at App.js:9)
in div (at App.js:7)
in App (at index.js:19)
in Provider (at index.js:18)
please help me...?

If you are running a test and you have something like alechill's answer, you'd need this to change in the test, for example:
let mockedStore = configureStore([])({});
test('some test', () => {
const wrapper = mount(<SomeComponent foo="bar" />, {
context: { store: mockedStore },
childContextTypes: { store: PropTypes.object.isRequired }
});
expect(.... your tests here);
});

You must pass the store instance to Provider...
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root')
)

Related

× TypeError: Cannot read properties of undefined (reading 'getState')

I am a beginner learning react and redux. I wrote this demo about how to use connect.js in redux. Searching this kind of question but there is no right answer for my code. I got a undefined context. Is it typo? or I passed context in a wrong way? Thanks in advance. Here is my code.
index.js
import React from "react";
import ReactDOM from "react-dom";
import store from "./store";
import { Provider } from "react-redux";
import App from "./App";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
/store/index.js
import { createStore } from "redux";
import reducer from "./reducer.js";
const store = createStore(reducer);
export default store;
/store/reducer.js
import { ADD, SUB, MUL, DIV } from './constants.js'
// or initialState
const defaultState = {
counter: 0
}
function reducer(state = defaultState, action) {
switch (action.type) {
case ADD:
return {...state, counter: state.counter + action.num};
case SUB:
return {...state, counter: state.counter - action.num};
case MUL:
return {...state, counter: state.counter * action.num};
case DIV:
return {...state, counter: state.counter / action.num};
default:
return state;
}
}
export default reducer
connect.js
import React, { PureComponent } from "react";
import { StoreContext } from "./context";
export default function connect(mapStateToProps, mapDispatchToProps) {
return function enhanceHOC(WrappedCpn) {
class EnhanceCpn extends PureComponent {
constructor(props, context) {
super(props, context);
console.log('connect props', props);
console.log('connect context', context); // context is undefined here
this.state = {
storeState: mapStateToProps(context.getState()),
};
}
componentDidMount() {
this.unSubscribe = this.context.subscribe(() => {
this.setState({
counter: mapStateToProps(this.context.getState()),
});
});
}
componentWillUnmount() {
this.unSubscribe();
}
render() {
return (
<WrappedCpn
{...this.props}
{...mapStateToProps(this.context.getState())}
{...mapDispatchToProps(this.context.dispatch)}
/>
);
}
}
EnhanceCpn.contextType = StoreContext;
return EnhanceCpn;
};
}
context.js
import React from "react";
const StoreContext = React.createContext();
export {
StoreContext
}
App.js
import React, { PureComponent } from 'react'
import My from './pages/my'
export default class App extends PureComponent {
constructor(props, context) {
super(props, context);
console.log('APP props', props);
console.log('APP context', context); // context got value
}
render() {
return (
<div>
<My />
</div>
)
}
}
my.js
import React, { PureComponent } from 'react'
import { sub, mul } from '../store/actionCreators'
import connect from '../utils/connect'
class My extends PureComponent {
render() {
return (
<div>
<h3>my</h3>
<h3>counter: { this.props.counter }</h3>
<button onClick={e => this.props.subNum()}>-2</button>
<button onClick={e => this.props.mulNUm(5)}>*5</button>
</div>
)
}
}
const mapStateToProps = state => ({
counter: state.counter
})
const mapDispatchToProps = dispatch => ({
subNum: (num = -2) => {
dispatch(sub(num))
},
mulNUm: num => {
dispatch(mul(num))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(My)
actionCreators.js
import { ADD, SUB, MUL, DIV } from './constants.js'
export function add(num) {
return {
type: ADD,
num
}
}
export const sub = (num) => {
return {
type: SUB,
num
}
}
export const mul = (num) => ({
type: MUL,
num
})
export const div = num => ({
type: DIV,
num
})
constants.js
const ADD = 'ADD_ACTION'
const SUB = 'SUB_ACTION'
const MUL = 'MUL_ACTION'
const DIV = 'DIV_ACTION'
export { ADD, SUB, MUL, DIV }
From the docs, here is what it says with regards to Class.contextType:
The contextType property on a class can be assigned a Context object
created by React.createContext(). Using this property lets you
consume the nearest current value of that Context type using
this.context. You can reference this in any of the lifecycle methods
including the render function.
It seems that in your case, you are just missing passing your custom StoreContext to redux Provider with the context props
You need to do something like:
import React from "react";
import ReactDOM from "react-dom";
import store from "./store";
import { StoreContext } from "./context";
import { Provider } from "react-redux";
import App from "./App";
ReactDOM.render(
<Provider store={store} context={StoreContext}>
<App />
</Provider>,
document.getElementById("root")
);
See also https://react-redux.js.org/using-react-redux/accessing-store#providing-custom-context
I got the same issue and solved by creating store on the same file as the root component where Provider is applied. Example code below:
<Provider store={createStore(reducers)}>
<App />
</Provider>
Cannot read properties of undefined (reading 'getState')
Solution:
(1) make a file store.js in your redux folder and you can copy the code
import { createStore, applyMiddleware } from "redux";
import logger from 'redux-logger';
import rootReducer from "./root-reducer";
const middlewares = [logger];
const store = createStore(rootReducer, applyMiddleware(...middlewares));
export default store;
(2) then just import the file in index.js file
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
i got same issues
i just have to change were i have my configure store from this
import { configureStore } from "#reduxjs/toolkit";
import basketReducer from "../slices/basketSlice";
export const store = configureStore({
reducer: {
basket: basketReducer,
},
});
to this
import { configureStore } from "#reduxjs/toolkit";
import basketReducer from "../slices/basketSlice";
export default configureStore({
reducer: {
basket: basketReducer,
},
});
and this method works for me
i also find out when i play around with the code to understand why it happen and see if i broke it what will happen this is my finding and observation from it
note concerning redux
if you used the below as import in your _app.js
import { store } from "../stores/store";
then
the global store should be rewritten like this
export const store = configureStore({
reducer: {
basket: basketReducer,
},
});
but if you import it like this with out distructuring it[store] or without the curly bracket
import store from "../stores/store";
then
you should write the store like this
export default configureStore({
reducer: {
basket: basketReducer,
},
});
Both ways works for me

How to configure redux with next js?

I configured redux this way and it works.
This is the _app.js file reconfigured :
import App from 'next/app';
import { Provider } from 'react-redux';
import withRedux from 'next-redux-wrapper';
import store from '../redux/store';
import React from 'react';
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
const appProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {};
console.log(appProps);
return {
appProps: appProps
};
}
render() {
const { Component, appProps } = this.props;
return (
<Provider store={store}>
<Component {...appProps} />
</Provider>
);
}
}
const makeStore = () => store;
export default withRedux(makeStore)(MyApp);
This is the index.js file which I've connected to redux :
import { connect } from 'react-redux';
import { callAction } from '../redux/actions/main';
const Index = (props) => {
console.log(props);
return (
<div>
Index js state <button onClick={() => props.callAction()}>Call action</button>
</div>
);
};
const mapStateProps = (state) => ({
name: state.main.name
});
const mapDispatchProps = {
callAction: callAction
};
export default connect(mapStateProps, mapDispatchProps)(Index);
This is the rootReducer file which gets only one reducer named main :
import { main } from './main';
import { combineReducers } from 'redux';
export const rootReducer = combineReducers({
main: main
});
And this is the store.js file :
import { createStore } from 'redux';
import { rootReducer } from './reducers/rootReducer';
const store = createStore(rootReducer);
export default store;
It all works fine but it throws a warning in the console which says :
/!\ You are using legacy implementaion. Please update your code: use createWrapper() and wrapper.withRedux().
What changes to which files I need to make to fix the legacy implementation warning?
I solved the warning by changing the way I get redux states and actions in index.js and the way passing them in _app.js files by using the createWrapper and withRedux :
_app.js
import App from 'next/app';
import store from '../redux/store';
import { Provider } from 'react-redux';
import { createWrapper } from 'next-redux-wrapper';
class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
);
}
}
const makeStore = () => store;
const wrapper = createWrapper(makeStore);
export default wrapper.withRedux(MyApp);
index.js
import { callAction } from '../redux/action';
import { connect } from 'react-redux';
const Index = (props) => {
return (
<div>
hey {props.name}
<br />
<button onClick={() => props.callAction()}>Call action</button>
</div>
);
};
const mapState = (state) => {
return {
name: state.name
};
};
const mapDis = (dispatch) => {
return {
callAction: () => dispatch(callAction())
};
};
export default connect(mapState, mapDis)(Index);
I would like to leave this answer, it worked for me in TS
================== _app.tsx ==================
import type { AppProps } from 'next/app'
import { Provider } from 'react-redux';
import { createWrapper } from 'next-redux-wrapper';
import { store } from '../redux/store';
function MyApp({ Component, pageProps }: AppProps & { Component: { layout: any }}) {
const Layout = Component.layout || (({ children }) => <>{children}</>);
return (
<Provider store={store}>
<Layout>
<Component {...pageProps} />
</Layout>
</Provider>
);
}
MyApp.getInitialProps = async ({ Component, router, ctx }) => {
const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {};
return { pageProps };
}
const makeStore = () => store;
const wrapper = createWrapper(makeStore);
export default wrapper.withRedux(MyApp);
================== store.tsx ==================
import { applyMiddleware, createStore } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import { rootReducer } from '../reducers';
export const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(thunkMiddleware)),
);
export type AppDispatch = typeof store.dispatch;
================== reducers.tsx ==================
import * as redux from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { Action } from 'typesafe-actions';
import user from './user';
export const rootReducer = redux.combineReducers({
user,
});
export type AppThunkDispatch = ThunkDispatch<RootState, void, Action>;
export type RootState = ReturnType<typeof rootReducer>;
================== onereducer.tsx ==================
import { HYDRATE } from "next-redux-wrapper";
import { Reducer } from 'redux';
import { ActionType } from 'typesafe-actions';
import { USER_ACTIONS } from '../../actions/types';
import { IUserData } from './types';
const userState: IUserData = {
_id: '',
email: '',
password: '',
role: '',
};
const userReducer: Reducer<IUserData, ActionType<any>> = (
state = userState,
action,
) => {
switch (action.type) {
case HYDRATE:
return { ...state, ...action.payload.userData };
case USER_ACTIONS.SET_USER_DATA:
return { ...state, ...action.payload.userData };
default:
return { ...state };
}
};
export default userReducer;
PD: Still a work in progress but works!

How would I set up my reducer properly?

I'm trying to toggle my modal via redux but for some reason I'm getting an error that says: TypeError: Cannot read property 'isModalOpen' of undefined (pointing to the line isOpen: state.global.isModalOpen inside List.js) which means my root reducer isn't defined properly. But I'm sure I've defined it properly inside index.js file. What am I doing wrong and how would I be able to resolve this and use these actiontypes properly in order for it to work?
Note: The <Modal/> component is an npm package I installed, I didn't create it on my own.
Here's my List.js file:
import React, { Component } from 'react';
import Aux from '../../../../hoc/Aux';
import { connect } from 'react-redux';
import Modal from 'react-modal';
class List extends Component {
state = {
isOpen: false
}
componentWillMount() {
Modal.setAppElement('body');
}
toggleModal = () => {
this.setState({isActive:!this.state.isActive});
}
closeModal = () => {
this.setState({isActive: false});
}
render() {
return (
<Aux>
<CheckoutButton clicked={this.toggleModal}/>
<Modal isOpen={this.state.isOpen}>
<button onClick={this.closeModal}>Close</button>
</Modal>
</Aux>
);
}
}
const mapStateToProps = state => {
return {
isOpen: state.global.isModalOpen
}
};
const mapDispatchToProps = dispatch => {
return {
closeModalRedux: () => dispatch({type: CLOSE_MODAL})
}
};
export default connect(mapStateToProps, mapDispatchToProps)(List);
Here's my reducer.js file:
import React from 'react';
import * as actionTypes from '../action/NoNameAction';
const initialState = {
isModalOpen: false
};
const global = (state = initialState, action) => {
switch(action.type) {
case actionTypes.OPEN_MODAL:
return {
...state,
isModalOpen: true,
}
case actionTypes.CLOSE_MODAL:
return {
...state,
isModalOpen: false,
}
default:
return state;
}
};
export default global;
Here's my index.js file:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { Provider } from 'react-redux';
import { createStore, combineReducers } from 'redux';
import modalOpenReducer from './store/reducer/reducer';
const rootReducer = combineReducers({
isOpen: modalOpenReducer
});
const store = createStore(rootReducer);
ReactDOM.render(<Provider store={store}><App/></Provider>, document.getElementById('root'));
registerServiceWorker();
change the reducer file name to global.js and in the root.js import it as global instead of reducer.And also in the component,change to this.props.isOpen

React.js Redux actions and reducers do not get initated

I am having an issue with my React-Redux app. I have created Redux action & reducer and connected them to my React component. However, it seems like it never gets to my Redux action & reducer.
I put "debugger" in my action and reducer but those debuggers never get initiated.
Could you please help me fix this issue?
Am I missing something here?
Here the current codes in the following files:
Here is the code for my app.js:
import React, { Component } from 'react';
import { render } from 'react-dom';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import ReduxPromise from 'redux-promise';
import rootReducer from './reducers';
import App from './components/index';
const createStoreWithMiddleware = applyMiddleware(thunk, ReduxPromise)(createStore);
render(
<Provider store={createStoreWithMiddleware(rootReducer)}>
<App />
</Provider>,
document.getElementById('app')
);
Here is my code for index.js:
As you can see below, I put a debugger in the "componentDidMount()", and when In run it, it gets to that point.
import React, { Component } from 'react';
import { render } from 'react-dom';
import { connect } from 'react-redux';
import * as AllNewsApiActions from '../actions/newsApiActions';
import '../../css/style.css';
class App extends Component {
componentDidMount() {
this.props.getNewsApi();
debugger;
}
render() {
return (
<div>
<p>Hello from react</p>
<p>Sample Testing</p>
</div>
);
}
}
const mapStateToProps = (state) => ({
newNews: state.newNews
});
export default connect(mapStateToProps, {
...AllNewsApiActions
})(App);
Here is my action:
import * as types from './newsTypes';
const API_KEY = "6c78608600354f199f3f13ddb0d1e71a";
export const getNewsApi = () => {
debugger;
return (dispatch, getState) => {
dispatch({
type: 'API_REQUEST',
options: {
method: 'GET',
endpoint: `https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=${API_KEY}`,
actionTypes: {
success: types.GET_NEWS_API_SUCCESS,
error: types.GET_NEWS_API_ERROR
}
}
});
}
}
Here is my reducer:
import * as types from '../actions/newsTypes';
const initialState = {
newNews: []
};
const getNewsAPIReducer = (state = initialState, action) => {
switch(action.type) {
case types.GET_NEWS_API_SUCCESS:
debugger;
return { ...state, newNews: action.data };
case types.GET_NEWS_API_ERROR:
debugger;
return { ...state, error: action.data };
default: {
return state;
};
};
};
export default getNewsAPIReducer;
Here is the index.js file in the reducer folder:
import { combineReducers } from 'redux';
import NewsApiReducer from './newsApiReducer.js';
const rootReducer = combineReducers({
newNews: NewsApiReducer
});
export default rootReducer;

Problems to dispatch action in React/Redux

I´m pretty new to React and Redux and have some issue during my first steps with it. I tried to follow the examples in the Redux Doc´s, but it´s hard for me to understand everything, because every example is jumping between ES5 - 6 or even 7 syntax.
However, When I try to dispatch an action I got the following error
Uncaught TypeError: (0 , _index2.default) is not a function
Error Message
I know that SO Community doesn´t prefer so much code within one Question, but I don´t know where the problem is coming from. Sorry for that!
These is my Code:
Index.js
import 'babel-polyfill'
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import App from './containers/App'
import configureStore from './store/configureStore'
const store = configureStore()
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
My Store
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
import index from '../reducers'
export default function configureStore(preloadedState) {
const store = createStore(
index,
preloadedState,
applyMiddleware(thunkMiddleware, createLogger())
)
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers').default
store.replaceReducer(nextRootReducer)
})
}
return store
}
My Container Component
import React, { Component, PropTypes } from 'react'
import AddTodo from '../components/AddTodo'
import { connect } from 'react-redux'
import addItem from '../actions/index'
class App extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick(e){
console.log("click")
console.log(e);
const {dispatch} = this.props
dispatch(addItem(e));
}
render() {
return (
<div>
< h1 > Hallo </h1>
<AddTodo handleAddItem={this.handleClick}/>
</div>
)
}
}
App.propTypes = {
dispatch: PropTypes.func.isRequired
}
function mapStateToProps(state){
return {
AddTodo
}
}
export default connect (mapStateToProps)(App)
My Child Component:
import React, { Component, PropTypes } from 'react'
import addItem from '../actions/index'
export default class AddTodo extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
this.state = {newItem: ''}
}
onChange(e){
console.log("change")
console.log(e.target.value);
this.setState({newItem: e.target.value})
}
handleClick(e){
this.props.handleAddItem(this.state.newItem)
// const {dispatch} = this.props
// console.log("clickc")
// console.log(this.state.newItem);
// dispatch(addItem(this.state.newItem))
}
render() {
return (
<div>
<h3>Add Item </h3>
<input
type="text"
value={this.state.newItem}
onChange={this.onChange.bind(this)}
/>
<button onClick={this.handleClick}>Hallo</button>
</div>
)
}
}
The Reducer
export default (state = [], action) => {
switch (action.type){
case 'ADD_ITEM':
return action.item
}
}
And Finally the action
export function addItem(item){
console.log("addTOdo")
return {
type: 'ADD_ITEM',
item
}
}
I hope someone can help me here, sitting since several hours and don´t understand what is happening.
You are not exporting action creator as default. You need either
export default function addItem(item){
console.log("addTOdo")
return {
type: 'ADD_ITEM',
item
}
}
or
import {addItem} from '../actions/index'

Categories

Resources