【问题标题】:Invalid hook call error react-redux hooks无效的钩子调用错误 react-redux 钩子
【发布时间】:2021-08-21 14:57:19
【问题描述】:

我是 react-redux 的新手,我有一个问题。我查看了很多网站,但我无法解决这个错误。

错误文本在这里:

错误:无效的挂钩调用。钩子只能在体内调用 的一个功能组件。这可能发生在以下情况之一 原因:

  1. 您可能有不匹配的 React 版本和渲染器(例如 React DOM)
  2. 您可能违反了 Hooks 规则
  3. 您可能在同一个应用中拥有多个 React 副本

这是我的 index.js 文件:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {Provider} from 'react-redux';
import { createStore } from 'redux'
import all from './reducers/index'

const store = createStore(all)
ReactDOM.render(
  <React.StrictMode>
    <Provider store ={store}>
    <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

reportWebVitals();

这是我的 ../reducers/index.js 文件:

import {
  combineReducers
} from "redux";
import TodoReducer from "./TodoReducer";
import filterReducer from "./filterReducer";

const all = combineReducers({
  TodoReducer,
  filterReducer,
});

export default all;

filterReducer.js

    import { SHOW_ALL, VISIBILITY_FILTER } from "../actions/actionTypes"; 
    
    const filterReducer = (state = SHOW_ALL, action) => {
      switch (action.type) {
        case "VISIBILITY_FILTER":
          return action.filter;
        default:
          return state;
      }
    };
    
    export default filterReducer;

TodoReducer.js

 import { ADD_TODO, DELETE_TODO, TOGGLE_TODO } from "../actions/actionTypes";
    
    const TodoReducer = (state = [], action) => {
      switch (action.type) {
        case "ADD_TODO":
          return [
            ...state, //todos
            {
              id: action.id,
              text: action.text,
              completed: false,
            },
          ];
    
        case "TOGGLE_TODO":
          return state.map((todo) =>
            todo.id === action.id
              ? {
                  ...todo,
                  completed: !todo.completed,
                }
              : todo
          );
    
        case "DELETE_TODO":
          const numIndex = parseInt(action.id);
          return state.filter((todo) => todo.id !== numIndex);
        default:
          return state;
      }
    };
    
    export default TodoReducer;

actionTypes.js

export const ADD_TODO = 'ADD_TODO'
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const DELETE_TODO = 'DELETE_TODO'
export const SHOW_ALL = 'SHOW_ALL'
export const VISIBILITY_FILTER = 'VISIBILITY_FILTER'

actions.js

import { ADD_TODO, DELETE_TODO, TOGGLE_TODO, VISIBILITY_FILTER } from './actionTypes'

let TodoId = 0

export const addTodo = text => ({
    type: ADD_TODO,
    id: TodoId++,
    text
})

export const deleteTodo = (id) => ({
    type: DELETE_TODO,
    id: id
})

export const toggleTodo = (id) => ({
    type: TOGGLE_TODO,
    id: id
})

export const visibility_filter = filter => ({
  type: VISIBILITY_FILTER,
  filter
})

这里是 Todos.js(这个文件是我使用钩子的地方):

import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { addTodo } from "../actions/actions";

const Todos = () => {
  const todos = useSelector((state) => state.todos);

  const dispatch = useDispatch();

  const onAddTodo = () =>
    dispatch(
      addTodo(inputText),
      todos.map((todo) => <div key={todo.id}>{todo.text}</div>)
    );

  const [inputText, setInputText] = useState("");

  const inputTextHandler = (e) => {
    setInputText(e.target.value);
  };

  return (
    <div>
      <div className="form-group row">
        <div className="col-sm-10">
          <input
            onChange={inputTextHandler}
            value={inputText}
            type="text"
            className="form-control"
            id="inputEmail3"
            placeholder="add todo here"
          />
          <button
            type="button"
            onClick={() => setInputText("")}
            style={{ marginTop: "25px", marginRight: "15px" }}
            className="btn btn-danger"
          >
            Cancel
          </button>
          <button
            type="button"
            onClick={onAddTodo}
            style={{ marginTop: "25px" }}
            className="btn btn-success"
          >
            Add Todo
          </button>
        </div>
      </div>
    </div>
  );
};

export default Todos;

这里是 App.js:

import React from "react";
import Table from './components/Table'; // this file is empty for now
import Todos from './components/Todos'

function App() {
  return (
    <div className="App">
        <div className="container" style={{ marginTop: "80px"}} >
          <div className="row">
            <div className="col-lg-10 offset-lg-2 col-md-10 col-sm-12 col-xs-12">
              <Todos />
            </div>
            <Table />
          </div>
        </div>
      </div>
  );
}

export default App;

这是错误代码:

   8 | import all from './reducers/index'
   9 | 
  10 | const store = createStore(all,)
> 11 | ReactDOM.render(
  12 |   <React.StrictMode>
  13 |     <Provider store ={store}>
  14 |     <App />

可能有成千上万的错误,我正在努力应对 react 和 redux...

有什么问题?你能帮我吗?感谢您的关注。

【问题讨论】:

  • 你没有显示你在哪里访问或改变 redux 的数据,这就是错误的来源。请更新您的问题。
  • 能否请您显示 App.js?
  • @RamanNikitsenka 我编辑了问题,因此您可以看到 App.js 文件。感谢您的关注^^
  • @Shamxeed 我编辑了问题,感谢您的关注^^
  • 我在你的代码中没有看到任何犯罪,如果你能提供在某处复制的例子会很有帮助

标签: javascript reactjs redux react-redux react-hooks


【解决方案1】:

看了你的代码后,我看到了这个。

 <button
            type="button"
            onClick={(inputText = "")}
            style={{ marginTop: "25px", marginRight: "15px" }}
            className="btn btn-danger"
          >

问题在于:

onClick={(inputText = "")}

组件的状态只能通过创建状态时提供的函数来改变。 在您的情况下,该函数称为setInputText

您的按钮应如下所示:

const handleInputReset = () => setInputText('');

<button
                type="button"
                onClick={handleInputReset}
                style={{ marginTop: "25px", marginRight: "15px" }}
                className="btn btn-danger"
              >

参考: https://reactjs.org/docs/hooks-state.html

【讨论】:

  • 啊,是的,对。正如你所写的,我改变了,但我仍然得到同样的错误。
  • 你为什么不使用 react-redux 钩子而不是 mapDispatchToProps 和连接。你可以通过从 react-redux 导入 {useSelector, useDispatch} 来做到这一点。 const todos = useSelector(state =&gt; state.todos); const dispatch = useDispatch(); 你的 addTodo 函数应该返回一个像这样的对象:{type: 'todo-add', payload: title} 如果你想在点击按钮时使用那个动作,你应该使用这个:dispatch(addTodo); 而不是仅仅在第二个按钮上添加Todo。跨度>
  • 我使用了这些钩子,谢谢你的建议^^但仍然遇到同样的错误......
【解决方案2】:

首先,我想建议您改用使用 redux 的钩子,因为它们可以简化一切。

您可以从react-redux 导入useDispatchuseSelector 而不是connect,并使用它们(挂钩)来访问和改变您的redux 状态。

例如


import { useSelector, useDispatch } from "react-redux"

import { addTodo } from "../actions/actions"

export const Todo = () => {

  //accessing your list of todos
   const todos = useSelector(state => state.todos)
  
  //dispatching an action
  const dispatch = useDispatch()

   const onAddTodo = () => dispatch(addTodo(someData))


   return (
    <div>
      <div className="form-group row">
        <div className="col-sm-10">
          <input
            onChange={inputTextHandler}
            value={inputText}
            type="text"
            className="form-control"
            id="inputEmail3"
            placeholder="add todo here"
          />
          <button
            type="button"
            onClick={() => setinputText("")}
            style={{ marginTop: "25px", marginRight: "15px" }}
            className="btn btn-danger"
          >
            Cancel
          </button>
          <button
            type="button"
            onClick={onAddTodo}
            style={{ marginTop: "25px" }}
            className="btn btn-success"
          >
            Add Todo
          </button>
        </div>
      </div>
    </div>
  );
}

当一般使用钩子时,你需要了解一些东西,钩子是在主组件中声明的。

你不能在组件的另一个函数中声明一个钩子。

例如

让我们使用上面的代码作为示例。 在上面的代码中,我们定义了onAddTodo 函数,用于将操作分派到我们的 redux 状态。 我们如何执行上述代码是正确的,但在某些情况下我们可能会出错,react 当然会向我们抛出您当前面临的错误。

以下是使用钩子的错误方法,因为它是在不是主要组件的函数中声明或启动的。

const onAddTodo = () => {
   //WRONG...
   const dispatch = useDispatch()

   dispatch(addTodo(someData))
}

【讨论】:

  • 您好,感谢您的建议。我将使用这些建议 ^^ 但仍然存在相同的错误...我编辑了我的问题(我编辑了 Todos.js)。我要疯了...我尝试了很多东西。我将代码传递给新项目。我使用了您的建议,但仍然存在相同的错误...
  • 函数onAddTodo 没有添加新的待办事项,您只是重新插入旧的待办事项。将其更改为const onAddTodo = () =&gt; dispatch(addTodo(inputText)) 之类的内容,您可以使用const todos = useSelector((state) =&gt; state.todos) 显示通过映射从useSelector 获得的待办事项数组已添加的待办事项列表,例如{todos.map(todo =&gt; (&lt;div key={todo.id}&gt;{todo.title}&lt;/div&gt;)) ...。还可以分享您的操作页面和reducer todos lemme 的页面,看看那里的一切是否正确。
  • 嗨,我分享了我所有的文件^^。
猜你喜欢
  • 2020-02-20
  • 1970-01-01
  • 1970-01-01
  • 2020-04-21
  • 1970-01-01
  • 2020-09-10
  • 1970-01-01
  • 1970-01-01
  • 2021-03-30
相关资源
最近更新 更多