【问题标题】:React-redux: mapDispatchToProps does not dispatch actionReact-redux:mapDispatchToProps 不调度动作
【发布时间】:2021-11-25 10:02:40
【问题描述】:

我目前正在学习 redux,我正在尝试通过创建个人项目来学习。这个想法是创建一个食谱应用程序,用户可以在其中查看食谱、保存他们喜欢的食谱并将他们喜欢的食谱导出到购物清单。

为了渲染食谱,我使用了两个组件。一个组件是单个配方,另一个组件使用单个配方组件映射不同的配方。我将两者拆分为单独的组件的原因是我希望映射不同配方的组件能够被重用以映射用户最喜欢的配方(避免编写额外的代码来基本上执行相同的过程,即渲染一个数组里面有不同的食谱)。

为了保存最喜欢的食谱,我想使用 redux,因为这(据我了解)允许我将状态分派到不同的组件(使用 mapStateToProps 和连接)。这应该允许我(如果我错了,请纠正我)在映射不同食谱的组件中使用 favoriteRecipes 的状态。我还可以使用单个食谱组件中的状态来创建一个按钮,用于将不同的食谱添加到我的收藏夹中。由于某种原因,我无法让 dispatch/redux 工作。在 redux devtools 中,我可以看到初始状态(一个空数组),但是一旦我尝试将配方添加到 favoriteRecipe 数组中,什么都没有发生。我已经尝试让它工作了几天,但似乎没有任何效果。

提前感谢您帮助我!

第一个文件:/redux/favoriteRecipes/favoriteRecipesTypes.js

export const ADD_TO_FAVORITE_RECIPES = 'ADD_TO_FAVORITE_RECIPES';
export const REMOVE_FROM_FAVORITE_RECIPES = 'REMOVE_FROM_FAVORITE_RECIPES';
export const CLEAR_FAVORITE_RECIPES = 'CLEAR_FAVORITE_RECIPES';

第二个动作创建者:/redux/favoriteRecipes/favoriteRecipesActions/js

import { ADD_TO_FAVORITE_RECIPES, REMOVE_FROM_FAVORITE_RECIPES, CLEAR_FAVORITE_RECIPES  } from "./favoriteRecipesTypes";

export const addToFavoriteRecipes = (recipe) => {
    return {
        type: ADD_TO_FAVORITE_RECIPES,
        payload: recipe
    }
}

export const removeFromFavoriteRecipes = (recipe) => {
    return {
        type: REMOVE_FROM_FAVORITE_RECIPES,
        payload: recipe
    }
}

export const clearFavoriteRecipes = () => {
    return{
        type: CLEAR_FAVORITE_RECIPES
    }
}

第三个reducer:/redux/favoriteRecipes/favoriteRecipesReducer.js

import { ADD_TO_FAVORITE_RECIPES, REMOVE_FROM_FAVORITE_RECIPES, CLEAR_FAVORITE_RECIPES  } from "./favoriteRecipesTypes";

const initialFavoriteRecipes = {
    favoriteRecipes: []
}

const favoriteRecipesReducer = (state = initialFavoriteRecipes, action) => {
    switch(action.type) {
        case ADD_TO_FAVORITE_RECIPES:
            return {
                ...state,
                favoriteRecipes: [...state.favoriteRecipes, action.payload]
            }
        case REMOVE_FROM_FAVORITE_RECIPES: 
            return {
                ...state,
                favoriteRecipes: state.favoriteRecipes.filter(item => item !== action.payload)
            }
        case CLEAR_FAVORITE_RECIPES:
            return {
                ...state,
                favoriteRecipes: []
            }
        default:
            return state
    }
}


export default favoriteRecipesReducer;

第四个商店:/redux/store.js

import { createStore, applyMiddleware, compose} from "redux";
import favoriteRecipesReducer from "../redux/favoriteRecipes/favoriteRecipesReducer";
import logger from "redux-logger";

const store = 
createStore(
    favoriteRecipesReducer, 
    compose(
        applyMiddleware(logger), 
        window.devToolsExtension ? window.devToolsExtension() : f => f
        )
)
    


export default store;

第五:单个配方组件(我省略了一些部分以使其尽可能简短):

import React, {useState} from 'react';
import {connect} from 'react-redux';
import { addToFavoriteRecipes } from '../../redux/favoriteRecipes/favoriteRecipesActions';

export const Recipe = ({recipe}, props) => {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <div className="col-md-3">
            <div className="card bwm-card">
                <div className="card-title">
                    <h2>{recipe.title}</h2>
                </div>
                <img className="card-img-top" src={recipe.image} alt={recipe.title} />
                <div className="card-subtitle">
                    <h3>
                        <b>Type: {recipe.type} </b>
                    </h3>
                    <h3>
                        <b>Category: {recipe.category}</b>
                    </h3>
                    <h3>
                        <b>Cooking time: {recipe.cookingTime} minutes</b>
                    </h3>
                </div>
                <div>//omitted part: show or hide full recipe depending on the state of isOpen</div>
                <div>
                    <input type="submit" onClick={props.addToFavoriteRecipes} value="add to favorite recipe" />
                </div>
            </div>
        </div>
    );
};


const mapStateToProps = state => {
    return{
        favoriteRecipes: state.favoriteRecipes
    }
}

const mapDispatchToProps = dispatch => {
    return{
        addToFavoriteRecipes: () => dispatch(addToFavoriteRecipes())
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Recipe)

【问题讨论】:

  • 通常请注意,您使用的是非常古老的 Redux 风格。现代 Redux 使用钩子而不是连接,并且没有 switch..case 减速器、ACTION_TYPES、不可变减速器逻辑或 createStore。查看官方 Redux 教程redux.js.org/tutorials/essentials/part-1-overview-concepts 了解更多信息。

标签: javascript reactjs redux react-redux


【解决方案1】:

React 组件只接收一个 props 参数,但您正试图从第二个未定义的参数中解构动作。 mapStateToPropsmapDispatchToProps,就像它们的名字所暗示的那样,将这些值映射到 props 对象上。它们可以从props 对象中解构。

export const Recipe = ({
  addToFavoriteRecipes, // <-- destructure from props
  recipe,
}) => {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div className="col-md-3">
      <div className="card bwm-card">
        <div className="card-title">
          <h2>{recipe.title}</h2>
        </div>
        <img className="card-img-top" src={recipe.image} alt={recipe.title} />
        <div className="card-subtitle">
          <h3>
            <b>Type: {recipe.type}</b>
          </h3>
          <h3>
            <b>Category: {recipe.category}</b>
          </h3>
          <h3>
            <b>Cooking time: {recipe.cookingTime} minutes</b>
          </h3>
        </div>
        <div>
          // omitted part: show or hide full recipe depending on the state of isOpen
        </div>
        <div>
          <input
            type="submit"
            onClick={addToFavoriteRecipes} // <-- pass as callback
            value="add to favorite recipe"
          />
        </div>
      </div>
    </div>
  );
};

【讨论】:

    【解决方案2】:

    感谢大家的回答!

    就在我提出问题之后,我得到了它的工作方式。

        import React, {useState} from 'react';
    import {connect} from 'react-redux';
    import { addToFavoriteRecipes } from '../../redux/favoriteRecipes/favoriteRecipesActions';
    import store from '../../redux/store';
    
    export const Recipe = ({recipe}) => {
        const [isOpen, setIsOpen] = useState(false);
    
        return (
            <div className="col-md-3">
                <div className="card bwm-card">
                    <div className="card-title">
                        <h2>{recipe.title}</h2>
                    </div>
                    <img className="card-img-top" src={recipe.image} alt={recipe.title} />
                    <div className="card-subtitle">
                        <h3>
                            <b>Type: {recipe.type} </b>
                        </h3>
                        <h3>
                            <b>Category: {recipe.category}</b>
                        </h3>
                        <h3>
                            <b>Cooking time: {recipe.cookingTime} minutes</b>
                        </h3>
                    </div>
                    <div>
                        {isOpen ? (
                            <div className="card-body">
                                <div className="card-body-ingredient">
                                    {recipe.ingredients.map((ingredient, i) => {
                                        return (
                                            <table>
                                                <tr>
                                                    <th>Quantity</th>
                                                    <th>Ingredient</th>
                                                </tr>
                                                <tr>
                                                    <th>{ingredient[0]}</th>
                                                    <th>{ingredient[1]}</th>
                                                </tr>
                                            </table>
                                        );
                                    })}
                                </div>
                                <div className="card-body-cookingsteps">
                                    {recipe.cookingSteps.map((cookingstep, i) => {
                                        return <p>{cookingstep}</p>;
                                    })}
                                </div>
                                <div>
                                    <button
                                        type="submit"
                                        value="Hide full recipe"
                                        onClick={() => setIsOpen(false)}>
                                        Hide full recipe
                                    </button>
                                </div>
                            </div>
                        ) : (
                            <button
                                type="submit"
                                value="Show full recipe"
                                onClick={() => setIsOpen(true)}>
                                Show full recipe
                            </button>
                        )}
                    </div>
                    <div>
                        <input type="submit" onClick={() => store.dispatch(addToFavoriteRecipes(recipe))} value="add to favorite recipe" />
                    </div>
                </div>
            </div>
        );
    };
    
    

    这似乎违背了使用 mapStateToProps 和 mapDispatchToProps 的全部目的。

    【讨论】:

    • 是的,当您导入 store 对象并直接向其分派操作时,它就会发生这种情况。它会起作用,但我认为这样做通常被认为是反模式,而不是使用mapDispatchToPropsuseDispatch 钩子。对于功能组件,建议使用useDispatch 挂钩。与使用 useSelector 挂钩来选择状态类似。干杯。
    猜你喜欢
    • 1970-01-01
    • 2021-10-14
    • 2018-05-21
    • 2020-05-25
    • 1970-01-01
    • 1970-01-01
    • 2019-08-15
    • 1970-01-01
    相关资源
    最近更新 更多