TypeError: 无法读取未定义属性(读取“getState”)

6

我是一名初学者,正在学习React和Redux。我编写了一个有关如何在Redux中使用connect.js的演示代码。我搜索了这种问题,但没有找到适用于我的代码的正确答案。我得到了一个未定义的上下文。这是笔误吗?还是我以错误的方式传递了上下文?提前致谢。以下是我的代码。

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 }

你的 actionCreators 在哪里? - Ryan Le
你是想为了好玩或者其他原因创建自己的 connect HOC 吗?为什么不使用/利用 react-redux 呢?如果我没记错,React 已经弃用了 constructor(props, context) 这个签名。 - Drew Reese
我编辑了我的问题并发布了 actionCreators.js。是的,我正在尝试创建自己的 connect hoc。App.js 中的 constructor(props, context) 正在工作。 - Graham Quan
1
如果您正在学习Redux,不妨学习编写reducer的现代风格 - smac89
4个回答

8

以下是关于 Class.contextType 的说明,摘自文档:

在类上使用 contextType 属性,可以将一个由 React.createContext() 创建的 Context 对象分配给它。使用这个属性可以通过 this.context 消费该 Context 类型的最近当前值,您可以在任何生命周期方法中引用它,包括渲染函数。

看起来在你的情况下,你只是忘了将你自定义的 StoreContext 与 redux 的 Provider 组件一起传递给 context 属性。

你需要像这样做:

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

另请参阅https://react-redux.js.org/using-react-redux/accessing-store#providing-custom-context


感谢您的回答。"<StoreContext.Provider value={store}>"对我有效。 - Graham Quan
@GrahamQuan 如果这个答案解决了你的问题,你应该考虑将其标记为已接受的答案。 - Tom
你能为此创建一个沙盒吗? - Akshay Kumar

3

我遇到了同样的问题,我只需要更改我的配置存储位置,从这里开始。

import { configureStore } from "@reduxjs/toolkit";
import basketReducer from "../slices/basketSlice";
export const store = configureStore({
  reducer: {
    basket: basketReducer,
       },
         });

转换成这个

import { configureStore } from "@reduxjs/toolkit";
import basketReducer from "../slices/basketSlice";
export default configureStore({
 reducer: {
  basket: basketReducer,
    },
     });

这个方法对我很有效 我还发现当我玩弄代码以理解为什么会发生这种情况并查看是否破坏它,我的发现和观察

关于redux的注意事项 如果您在_app.js中使用以下导入

import { store } from "../stores/store";

那么全局存储应该像这样重写

 export const store = configureStore({
      reducer: {
        basket: basketReducer,
      },
    });

但是如果你像这样导入它,而不是解构它[store]或者没有花括号

import store  from "../stores/store";

那么你应该这样编写存储过程

 export default configureStore({
                reducer: {
                  basket: basketReducer,
                },
              });

对我来说,两种方式都可以。


2

我曾遇到同样的问题,并通过在应用Provider的根组件所在的同一文件中创建存储来解决。以下是示例代码:

 <Provider store={createStore(reducers)}>
    <App />
 </Provider>

0
无法读取未定义的属性(读取“getState”)
解决方案:
(1)在您的Redux文件夹中创建一个名为store.js的文件,然后您可以复制代码。
    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) 然后只需在 index.js 文件中导入该文件

     <Provider store={store}>
          <BrowserRouter>
             <App />
          </BrowserRouter>
      </Provider>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接