【问题标题】:How to add action to functional component using Redux and ReactJS如何使用 Redux 和 ReactJS 向功能组件添加操作
【发布时间】:2019-10-18 11:54:36
【问题描述】:

我尝试将操作“addProduct”添加到组件中。但是我点击的时候看到了错误,帮我解决一下,先谢谢了!

我不明白我的错误在哪里(

import { bindActionCreators } from "redux";
import { addProduct } from "../actions/addProduct";

const ProductListItem = ({ product }) => {
  return (
    <div className="product">
      <img className="product_img" src={product.image} />
      <p>{product.name}</p>
      <p className="bold">$ {product.price}</p>
      <button className="add_card" onClick={() => addProduct(product)}>
        {" "}
        add to cart
      </button>
    </div>
  );
};

export default ({ products = [] }) =>
  products.map((product, i) => {
    return <ProductListItem key={i} product={product} />;
  });


const mapDispatchToProps = dispatch => {
  return bindActionCreators({ addProduct }, dispatch);
};

connect(null,
  mapDispatchToProps
)(ProductListItem);
[enter image description here][1]

【问题讨论】:

  • connect 的第一个参数应该是一个函数。通常它被称为 mapStateToProps 从你的状态返回数据并使其在组件道具中可用
  • @Anon,解决问题有运气吗? :o

标签: javascript reactjs redux


【解决方案1】:

要正确添加您的操作,您需要使用 redux 连接函数将调度映射到您的组件。

function connect(mapStateToProps?, mapDispatchToProps?, ...)

第一个参数用于将 state 映射到 props。

const mapStateToProps = state => {
    return { products: state.products, cart: state.cart };
};

const Item = connect(mapStateToProps)(MyItemComp)

现在在您的组件中,您可以访问 productscart 作为道具。您可以使用this.props.products访问它

第二个参数用于允许你的组件调度动作。

function mapDispatchToProps(dispatch) {
  return {
    addProduct: e => dispatch(addProduct(e))
  };
}

const Item = connect(mapStateToProps, mapDispatchToProps)(MyItemComp)

现在我们可以通过执行 addProduct(product) 来从我们的组件中调度操作。

connect 可以像这样使用

// will only access props, won't dispatch any actions
const Item = connect(mapStateToProps)(MyItemComp)
// dispatch actions, won't access props
const Item = connect(null, mapDispatchToProps)(MyItemComp)
// will access props and dispatch actions
const Item = connect(mapStateToProps, mapDispatchToProps)(MyItemComp)

现在让我们修复您的组件

 import { addProduct } from "../actions/addProduct";
 import { connect } from "react-redux";

// add addProduct as a second parameter, to be able to dispatch actions from here
const ProductListItem = ({ product, addProduct }) => {
  return (
    <div className="product">
      <img className="product_img" src={product.image} />
      <p>{product.name}</p>
      <p className="bold">$ {product.price}</p>
      <button className="add_card" onClick={() => addProduct(product)}>
       {" "}
       add to cart
      </button>
    </div>
  );
};

改变

export default ({ products = [] }) =>
  products.map((product, i) => {
  return <ProductListItem key={i} product={product} />;
});

// export ProductListItem
export default ({ products = [], addProduct }) =>
  products.map((product, i) => {
    return <ProductListItem key={i} product={product} addProduct={addProduct}/>;
});

现在我们需要附加 props 和 dispatcher

// import the above export
import ProductListItem from './ProductListItem';

const ProductList = connect(
  mapStateToProps,
  mapDispatchToProps
)(ProductListItem);

在你的父组件中

render() {
  ...
  return(
    <div>
      ...
      <ProductList />
      ...
    </div>
   )
}

演示

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.1/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.0.3/react-redux.min.js"></script>
<script src="http://wzrd.in/standalone/uuid%2Fv1@latest"></script>
<link
      href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
      rel="stylesheet"
      integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN"
      crossorigin="anonymous"
    />
<style>
  .product {
     display: flex;
     flex-direction: row;
     border-bottom-style: solid;
     margin-bottom: 5px;
     margin-top: 10px;
   }

   img.product_img {
     width: 30px;
   }

   .badge {
     padding-left: 9px;
     padding-right: 9px;
     -webkit-border-radius: 9px;
     -moz-border-radius: 9px;
     border-radius: 9px;
    }

    .label-warning[href],
    .badge-warning[href] {
      background-color: #c67605;
    }
    #lblCartCount {
      font-size: 12px;
      background: #ff0000;
      color: #fff;
      padding: 0 5px;
      vertical-align: top;
      margin-left: 1px;
    }
 </style>
    
 <div id="root"></div>
    
<script type="text/babel">
const { Provider, connect } = ReactRedux;
const { applyMiddleware, createStore, combineReducers } = Redux;

function addProduct(payload) {
  return { type: 'ADD_PRODUCT', payload };
}

const initialState = {
  products: [
    {
      id: 1,
      name: 'Avocado',
      price: 1.5,
      image: 'https://img.icons8.com/metro/26/000000/avocado.png',
      quantity: 0
    },
    {
      id: 6,
      name: 'Bread',
      price: 1,
      image: 'https://img.icons8.com/metro/26/000000/bread.png',
      quantity: 0
    },
    {
      id: 2,
      name: 'Milk',
      price: 1.8,
      image: 'https://img.icons8.com/metro/26/000000/milk-bottle.png',
      quantity: 0
    }
  ],
  cart: []
};

function rootReducer(state = initialState, action) {
  if (action.type == 'ADD_PRODUCT') {
    return {
      ...state,
      cart: [...state.cart, action.payload]
    };
  }
  return state;
}

const store = createStore(rootReducer);

const mapStateToProps = state => {
  return { products: state.products, cart: state.cart };
};

function mapDispatchToProps(dispatch) {
  return {
    addProduct: e => dispatch(addProduct(e))
  };
}

const CartItems = ({ cart }) => {
  return (
    <div>
      <i class="fa fa-shopping-cart" />
      <span class="badge badge-warning" id="lblCartCount">
        {cart.length}
      </span>
    </div>
  );
};

const ProductListItem = ({ product, addProduct }) => {
  return (
    <div className="product">
      <img className="product_img" src={product.image} />
      <p>{product.name}</p>
      <p style={{ marginLeft: 5, marginRight: 5 }} className="bold">${product.price}</p>
      <button className="add_card" onClick={() => addProduct(product)}>
        {' '}
        add to cart
      </button>
    </div>
  );
};

const Cart = connect(mapStateToProps)(CartItems);
const Products = ({ products = [], addProduct }) =>
  products.map((product, i) => {
    return <ProductListItem key={i} product={product} addProduct={addProduct}/>;
  });

const ProductList = connect(
  mapStateToProps,
  mapDispatchToProps
)(Products);

class App extends React.Component {
  render() {
    return (
      <div>
        <Cart />
        <ProductList />
      </div>
    );
  }
}
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
</script>

【讨论】:

    【解决方案2】:

    我做了一些更新,并附上了我正在做的一个例子。

    1. 看来您需要从react-redux 导入connect
    2. 现在你需要从你的道具中解构addProduct 你已经用mapDispatchToProps 映射了它
    3. 我将mapDispatchToProps 重新定义为不使用bindActionCreators, 这只是一个偏好问题,但任何一个都应该有效。
    4. 在您的connect() 中,我使用...rest 解构所有剩余的道具 并将其作为道具传播到返回中。

    ..

    家长

    import React from "react"
    import ProductListItem from "./ProductListItem"
    
    const products = [1,2,3] <-- data might be in redux-state but I'll use this for simplicity.
    
    const TestHolder = () => {
        return(
            <div>
                <ProductListItem products={products}/>
            </div>
        )
    }
    
    export default TestHolder
    

    产品列表项

    import React from "react"
    import { connect } from "react-redux"
    import { addProduct } from "../../actions/testActions";
    
    const ProductListItem = ({ product, addProduct }) => {
      return (
        <div className="product">
          <button className="add_card" onClick={() => addProduct()}>
            {" "}
            add to cart
          </button>
        </div>
      );
    };
    
    const mapDispatchToProps = dispatch => {
      return {
        addProduct: (product) => {
          dispatch(addProduct(product))
        }
      }
    };
    
    export default connect(null, mapDispatchToProps)(({ products = [], ...rest }) =>
      products.map((product, i) => {
        return <ProductListItem key={i} product={product} {...rest} />;
      }))
    

    动作创建者

    export const addProduct = () => {
        return {
            type: "Woof"
        }
    }
    

    您可能已经将您的动作创建者格式错误并使其成为普通对象而不是返回对象的函数。

    【讨论】:

    • TypeError: addProduct 不是函数
    • @Anon,嗯,有道理,您没有在 connect() 中明确返回 ProductListItem。而是使用 .map 来返回它们的数组。当你这样做时,动作创建者不会明确地作为道具传递。我实际上从未见过有人在 connect() 中这样做。你能试试我上面更新的答案,让我知道这是否有效吗? :)
    • 是的,我尝试...现在我看到 TypeError: Object(...) is not a function
    • @Anon,嗯,我在本地运行它,它似乎按预期工作。我已经更新了上面的解决方案,以显示我正在做的一个快速示例。你能和我分享一下你的代码有什么不同吗?
    • 这个const products = [1,2,3]不应该在商店里吗?
    猜你喜欢
    • 2016-11-13
    • 2019-01-11
    • 2017-11-19
    • 1970-01-01
    • 2023-04-10
    • 2018-03-16
    • 1970-01-01
    • 1970-01-01
    • 2020-09-18
    相关资源
    最近更新 更多