In my stateless component DebtType I am triggering 'handleChangeDebtType'
on the onChange event:
const DebtType = (options, handleChangeDebtType) => {
console.log(options);
return (
<select onChange={handleChangeDebtType}>
{options.options.map(option => {
return <option>{option.label}</option>;
})}
</select>
);
};
export default DebtType;
This DebtType component is called in MyContainer:
import React, { Component } from "react";
import DebtType from "./DebtType";
import mockOptions from "./mockData.json";
import ClearDebtType from "./ClearDebt";
import { reduxForm } from "redux-form";
import { connect } from "react-redux";
class MyContainer extends Component {
handleChangeDebtType= () => {
//save selected debttype to store
console.log("handleChangeDebtType");
}
render() {
return (
<div>
<DebtType
options={mockOptions.DEBT_TYPE}
handleChangeDebtType={this.handledChangeDebtType}
/>
<ClearDebtType options={mockOptions.CLEARDEBT_TYPE} />
</div>
);
}
}
const mapStateToProps = state => ({
//selectedDebtType:
});
MyContainer = connect(
mapStateToProps,
undefined
)(MyContainer);
export default reduxForm({
form: "simple" // a unique identifier for this form
})(MyContainer);
//export default ;
How can I trigger the 'handleChangeDebtType' event? Currently it is not firing.
updated the handleChangeDebtType function, minor typo in ur handleChangeDebtType function name
import React, { Component } from "react";
import DebtType from "./DebtType";
import mockOptions from "./mockData.json";
import ClearDebtType from "./ClearDebt";
import { reduxForm } from "redux-form";
import { connect } from "react-redux";
class MyContainer extends Component {
handleChangeDebtType = () => {
//save selected debttype to store
console.log("handleChangeDebtType");
};
render() {
return (
<div>
<DebtType
options={mockOptions.DEBT_TYPE}
handleChangeDebtType={() => {
this.handleChangeDebtType();
}}
/>
<ClearDebtType options={mockOptions.CLEARDEBT_TYPE} />
</div>
);
}
}
const mapStateToProps = state => ({
//selectedDebtType:
});
MyContainer = connect(
mapStateToProps,
undefined
)(MyContainer);
export default reduxForm({
form: "simple" // a unique identifier for this form
})(MyContainer);
//export default ;
de-structured the component props
import React from "react";
const DebtType = ({options, handleChangeDebtType}) => {
console.log(options);
return (
<select onChange={handleChangeDebtType}>
{options.map(option => {
return <option key={option.label}>{option.label}</option>;
})}
</select>
);
};
export default DebtType;
All you forgot to do was add de-structured props to the function component DebtType , in your example the handleChangeDebtType was not being picked up as a function that's why it wasn't firing. Also don't forget to add the key when looping though array.
Link to fixed codesandbox : https://codesandbox.io/s/kkxz980qw5
import React from "react";
const DebtType = ({ options, handleChangeDebtType }) => {
console.log(options);
return (
<select onChange={handleChangeDebtType}>
{options.map((option, index) => {
return <option key={index}>{option.label}</option>;
})}
</select>
);
};
export default DebtType;
Related
I am building an app with react / redux for managing Collection of Electronic equipment (=donations). I have several routes that their functionality - is similiar - fetching entity (it could be volunteer, donor etc) data and show it in a table.
the volunteer route:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import { requestVolunteerData } from '../actions/entitiesAction';
import { volenteerColumns as columns } from '../utils/entitiesColumns/volenteerColumns';
import '../container/App.css';
import Table from '../components/Table/Table';
import Loading from '../components/Loading/Loading';
const mapStateToProps = state => {
return {
entities: state.requestEntitiesReducer.entities,
isPending: state.requestEntitiesReducer.isPending,
error: state.requestEntitiesReducer.error
}
}
const mapDispatchToProps = dispatch => {
return {
onRequestEntities: () => dispatch(requestVolunteerData())
}
}
class Volenteer extends Component{
componentDidMount () {
this.props.onRequestEntities();
}
render () {
const { entities, isPending} = this.props;
return isPending ?
<Loading />
:
(
<div className='tc'>
<h1 className='f2'>רשימת מתנדבים</h1>
<Table data={ entities } columns={ columns } />
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Volenteer);
and a consumer route look like this:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import { requestConsumerData } from '../actions/entitiesAction';
import { consumerColumns as columns } from '../utils/entitiesColumns/consumerColumns';
import '../container/App.css';
import Table from '../components/Table/Table';
import Loading from '../components/Loading/Loading';
const mapStateToProps = state => {
return {
entities: state.requestEntitiesReducer.entities,
isPending: state.requestEntitiesReducer.isPending,
error: state.requestEntitiesReducer.error
}
}
const mapDispatchToProps = dispatch => {
return {
onRequestEntities: () => dispatch(requestConsumerData())
}
}
class Consumer extends Component{
componentDidMount () {
this.props.onRequestEntities();
}
render () {
const { entities, isPending} = this.props;
return isPending ?
<Loading />
:
(
<div className='tc'>
<h1 className='f2'>רשימת נזקקים</h1>
<Table data={ entities } columns={ columns }/>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Consumer);
As you can see, they both have the same logic and the differences are:
the action
the Entity name for the h1 tag
the columns object
the data of course
so I tried to implement an HOC which look like this:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import '../container/App.css';
import Table from '../Table/Table';
import Loading from '../Loading/Loading';
export default function WithEntity (EntityComponent, action, columns, name) {
const mapStateToProps = state => {
return {
isPending: state.requestEntitiesReducer.isPending,
entities: state.requestEntitiesReducer.entities,
error: state.requestEntitiesReducer.error
}
}
const mapDispatchToProps = dispatch => {
return {
onRequestEntities: () => dispatch(action)
}
}
class extends Component {
componentDidMount () {
this.props.onRequestEntities();
}
render() {
return (
<EntityComponent {...this.props} />
)
}
}
return connect(mapStateToProps, mapDispatchToProps)(EntityComponent);
}
and the volunteer should look like:
const volunteerHoc = WithEntity (volunteer, action, columns, name);
const consumerHoc = WithEntity (consumer, action, columns, name)
but I did not understand how to inject the Loading and Table components, and wht the name of the class inside the HOC should be-
should I use another HOC - something like WithLoader that receive the data from the first one and render the Loading and Table components with the proper data? just to mention that connect is HOC itself so I need to return the EntityComponent to the redux store :
return connect(mapStateToProps, mapDispatchToProps)(EntityComponent);
I Would appreciate any help
OK, I made it, the HOC takes a basic component, Expands the functionality (by adding methods and managing state for ex) and return a new (henanced) comp with this props.
lets create a simple volunteer comp:
import React, {Component} from 'react';
import { requestVolunteerData } from '../actions/entitiesAction';
import { volenteerColumns as columns } from '../utils/entitiesColumns/volenteerColumns';
import '../container/App.css';
import WithEntity from '../components/HOC/WithEntity.jsx';
import Table from '../components/Table/Table';
import Loading from '../components/Loading/Loading';
class Volenteer extends Component {
render() {
const { entities, isPending} = this.props;
return isPending ?
<Loading />
:
(
<div className='tc'>
<h1 className='f2'>רשימת מתנדבים</h1>
<Table data={ entities } columns={ columns } />
</div>
);
}
}
const VolenteerHOC = WithEntity(Volenteer, requestVolunteerData() );
export default VolenteerHOC;
now lets create the HOC WithEntity that managing the state and return the new cmop to redux state by connect:
import React, {Component} from 'react';
import { connect } from 'react-redux';
const WithEntity = (EntityComponent, action) => {
const mapStateToProps = state => {
return {
isPending: state.requestEntitiesReducer.isPending,
entities: state.requestEntitiesReducer.entities,
error: state.requestEntitiesReducer.error
}
}
const mapDispatchToProps = dispatch => {
return {
onRequestEntities: () => dispatch(action)
}
}
class NewCmoponent extends Component {
componentDidMount () {
this.props.onRequestEntities();
}
render() {
const { entities, isPending} = this.props;
return (
<EntityComponent {...this.props} />
)
}
}
return connect(mapStateToProps, mapDispatchToProps)(NewCmoponent );
}
export default WithEntity;
Now same route can be simply generated via this HOC.
check out this video:
https://www.youtube.com/watch?v=rsBQj6X7UK8
I have a button that adds counters. It works but its a matter of UI structuring. When I click Add a counter, adds individual counters.
What I need is to have the independent counters, perhaps by programmatically the same result instead of onClick the button, having the +/- like <button> {counter} </button> directly.
What I have:
What I need (without clicking the above button):
When I click + or - then
TypeError: this.props.onIncrement is not a function
onClick
src/js/components/Posts.js:33
30 | <div>
31 | <span>{this.props.value}</span>
32 | <button
> 33 | onClick={() => this.props.onIncrement()}>
| ^ 34 | +
35 | </button>
Code:
Counter.js
// ./src/js/components/Counter.js
import React, { Component } from 'react';
class Counter extends Component {
render() {
return (
<div>
<span>{this.props.value}</span>
<button
onClick={() => this.props.onIncrement()}>
+
</button>
<button
onClick={() => this.props.onDecrement()}>
-
</button>
</div>
);
}
}
export default Counter;
Action
// ./src/js/actions/counters.js
export const increment = (id) => {
return {
type: "INCREMENT",
id
};
};
export const decrement = (id) => {
return {
type: "DECREMENT",
id
};
};
export const add_counter = () => {
return {
type: "ADD_COUNTER"
};
};
AddButton.js:
import React from 'react';
import { add_counter } from '../actions/counters';
import { connect } from 'react-redux';
const AddButton = ({dispatch}) => (
<button
onClick={() => {
dispatch(add_counter());
}}>
Add a counter
</button>
);
export default connect()(AddButton);
counterlist.js
// ./src/js/components/CounterList.js
import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from '../actions/counters';
import Counter from './Counter';
const CounterList = ({
counters,
onIncrement,
onDecrement
}) => (
<ul>
{counters.map(counter =>
<Counter
key={counter.id}
value={counter.count}
onIncrement={() => onIncrement(counter.id)}
onDecrement={() => onDecrement(counter.id)}
/>
)}
</ul>
);
const mapStateToProps = (state) => {
return {
counters: state
};
};
const mapDispatchToProps = (dispatch) => {
return {
onIncrement: (id) => dispatch(increment(id)),
onDecrement: (id) => dispatch(decrement(id))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(CounterList);
Posts.js
import React, {Component} from 'react';
import logo from '../../logo.svg';
import '../../App.css';
import { connect } from 'react-redux';
import Counter from './Counter'
import AddButton from './AddButton';
class Posts extends Component {
constructor(props) {
super(props);
this.state = {
response: ''
};
}
render() {
return (
<div className="App">
{Array.isArray(this.state.response) &&
this.state.response.map(resIndex => <div>
<AddButton/>
<Counter/>
{onIncrement(counter.id)}>
<h5> { resIndex.title } </h5>
<h5> { resIndex.body } </h5>
</div>
)}
</div>
)
}
}
export default connect()(Posts);
You can do what you want with Redux given what you have above, but you won't have access to your counter_id inside your Posts component to increment/decrement a given counter. You would need to somehow get that information into your Posts component, which is where you wanted to call increment/decrement programatically. This project would need restructured to resolve that issue. I would ditch Redux as it appears you're learning and I would focus on mastering state and props within React without Redux first.
This is how you would do it with Redux if you had access to a counter's ID. You'll pass into your component the increment/decrement functions as props by using the mapDispatchToProps function. You didn't have that in your Posts component which is why it was telling you your function was not defined. You have to have mapDispatchToProps for it to define the function on this.props.
I wasn't sure what you're doing to need to call this.props.onIncrement, so I just put it in componentDidMount for demonstration purposes.
import React, {Component} from 'react';
import { connect } from 'react-redux';
import Counter from './Counter';
import { increment, decrement } from '../actions/counters';
import AddButton from './AddButton';
class Posts extends Component {
constructor(props) {
super(props);
this.state = {
response: ''
};
}
componentDidMount() {
this.props.onIncrement(<your_counter_id>);
}
render() {
return (
<div className="App">
{Array.isArray(this.state.response) &&
this.state.response.map(resIndex => <div>
<AddButton/>
<Counter/>
<h5> { resIndex.title } </h5>
<h5> { resIndex.body } </h5>
</div>
)}
</div>
)
}
}
const mapDispatchToProps = (dispatch) => {
({
onIncrement: (id) => dispatch(increment(id)),
onDecrement: (id) => dispatch(decrement(id))
});
};
export default connect({}, mapDispatchToProps)(Posts);
Tried to look through similar questions, but didn't find similar issues.
I am trying to implement sorts by name and amount in my app, this event is triggered in this component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { sortByExpenseName, sortByExpenseAmount } from '../actions/expensesFilters';
class ExpensesListFilter extends Component {
onSortByExpenseName = () => {
this.props.sortByExpenseName();
};
onSortByExpenseAmount = () => {
this.props.sortByExpenseAmount();
}
render() {
return (
<div>
<span>Expense Name</span>
<button onClick={this.onSortByExpenseName}>Sort me by name</button>
<button onClick={this.onSortByExpenseAmount}>Sort me by amount</button>
</div>
)
}
}
const mapDispatchToProps = (dispatch) => ({
sortByExpenseName: () => dispatch(sortByExpenseName()),
sortByExpenseAmount: () => dispatch(sortByExpenseAmount()),
});
export default connect(null, mapDispatchToProps)(ExpensesListFilter);
for that I am using following selector:
export default (expenses, { sortBy }) => {
return expenses.sort((a, b) => {
if (sortBy === 'name') {
return a.name < b.name ? 1 : -1;
} else if (sortBy === 'amount') {
return parseInt(a.amount, 10) < parseInt(b.amount, 10) ? 1 : -1;
}
});
};
I run this selector in mapStateToProps function for my ExpensesList component here:
import React from 'react';
import { connect } from 'react-redux';
import ExpensesItem from './ExpensesItem';
// my selector
import sortExpenses from '../selectors/sortExpenses';
const ExpensesList = props => (
<div className="content-container">
{props.expenses && props.expenses.map((expense) => {
return <ExpensesItem key={expense.id} {...expense} />;
}) }
</div>
);
// Here I run my selector to sort expenses
const mapStateToProps = (state) => {
return {
expenses: sortExpenses(state.expensesData.expenses, state.expensesFilters),
};
};
export default connect(mapStateToProps)(ExpensesList);
This selector updates my filter reducer, which causes my app state to update:
import { SORT_BY_EXPENSE_NAME, SORT_BY_EXPENSE_AMOUNT } from '../actions/types';
const INITIAL_EXPENSE_FILTER_STATE = {
sortBy: 'name',
};
export default (state = INITIAL_EXPENSE_FILTER_STATE, action) => {
switch (action.type) {
case SORT_BY_EXPENSE_NAME:
return {
...state,
sortBy: 'name',
};
case SORT_BY_EXPENSE_AMOUNT:
return {
...state,
sortBy: 'amount',
};
default:
return state;
}
};
Sort event causes my state to update, the expenses array in my expenses reducer below is updated and sorted by selector, BUT the ExpensesList component doesn't re-render after my expenses array in state is updated.
What I want my ExpensesList component to do, is to re-render with sorted expenses array and sort ExpensesItem components in list.
What could be the reason why it fails? Pretty sure I am missing out something essential, but can't figure out what. My expenses reducer:
import { FETCH_EXPENSES } from '../actions/types';
const INITIAL_STATE = {};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case FETCH_EXPENSES:
return {
...state,
expenses: action.expenses.data,
};
default:
return state;
}
};
All these components are childs to this parent component:
import React from 'react';
import ExpensesListFilter from './ExpensesListFilter';
import ExpensesList from './ExpensesList';
const MainPage = () => (
<div className="box-layout">
<div className="box-layout__box">
<ExpensesListFilter />
<ExpensesList />
</div>
</div>
);
export default MainPage;
App.js file (where I run startExpenseFetch)
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import 'normalize.css/normalize.css';
import AppRouter, { history } from './routers/AppRouter';
import configureStore from './store/configureStore';
import LoadingPage from './components/LoadingPage';
import { startExpenseFetch } from './actions/expensesData';
import './styles/styles.scss';
const store = configureStore();
const jsx = (
<Provider store={store}>
<AppRouter />
</Provider>
);
let hasRendered = false;
const renderApp = () => {
if (!hasRendered) {
ReactDOM.render(jsx, document.getElementById('app'));
hasRendered = true;
}
};
store.dispatch(startExpenseFetch()).then(() => {
renderApp();
});
ReactDOM.render(<LoadingPage />, document.getElementById('app'));
Rest of files:
ExpenseItem Component:
import React from 'react';
const ExpenseItem = ({ amount, name }) => (
<div>
<span>{name}</span>
<span>{amount}</span>
</div>
);
export default ExpenseItem;
Action creators:
expensesData.js
import axios from 'axios';
import { FETCH_EXPENSE } from './types';
// no errors here
const ROOT_URL = '';
export const fetchExpenseData = expenses => ({
type: FETCH_EXPENSE,
expenses,
});
export const startExpenseFetch = () => {
return (dispatch) => {
return axios({
method: 'get',
url: `${ROOT_URL}`,
})
.then((response) => {
dispatch(fetchExpenseData(response));
console.log(response);
})
.catch((error) => {
console.log(error);
});
};
};
expensesFilters.js
import { SORT_BY_EXPENSE_NAME, SORT_BY_EXPENSE_AMOUNT } from './types';
export const sortByExpenseName = () => ({
type: SORT_BY_EXPENSE_NAME,
});
export const sortByExpenseAmount = () => ({
type: SORT_BY_EXPENSE_AMOUNT,
});
configureStores.js file
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import expensesDataReducer from '../reducers/expensesData';
import expensesFilterReducer from '../reducers/expensesFilters';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default () => {
const store = createStore(
combineReducers({
expensesData: expensesDataReducer,
expensesFilters: expensesFilterReducer,
}),
composeEnhancers(applyMiddleware(thunk))
);
return store;
};
AppRouter.js file
import React from 'react';
import { Router, Route, Switch, Link, NavLink } from 'react-router-dom';
import createHistory from 'history/createBrowserHistory';
import MainPage from '../components/MainPage';
import NotFoundPage from '../components/NotFoundPage';
export const history = createHistory();
const AppRouter = () => (
<Router history={history}>
<div>
<Switch>
<Route path="/" component={MainPage} exact={true} />
<Route component={NotFoundPage} />
</Switch>
</div>
</Router>
);
export default AppRouter;
Don't you have a typo on your call to your selector? :)
// Here I run my selector to sort expenses
const mapStateToProps = (state) => {
return {
expenses: sortExpenses(state.expensesData.expenses, state.expnsesFilters),
};
};
state.expnsesFilters look like it should be state.expensesFilters
Which is one of the reasons you should make your sortExpenses selector grab itself the parts of the state it needs and do it's job on its own. You could test it isolation and avoid mistakes like this.
I found a reason why it happens, in my selector I was mutating my app's state. I wasn't returning a new array from it, and was changing the old one instead, that didn't trigger my vue layer to re-render. Fixed it and it works now.
I've set up my Redux to capture a user selection from a webshop (item, size, price) and send it to another Cart component. This is working perfectly, but I want to capture an image of the item and send it to Cart. Within each product page where you can add an item to the cart there is an image that I also would like to send with the user selection. This is an example of the product page component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { addCart } from '../../actions';
import SeltzShirt from './seltzshirt.jpg';
import Slideone from './slideSeltzOne';
import Slidetwo from './slideSeltzTwo';
import RightArrow from './rightarrow';
import LeftArrow from './leftarrow';
export class ProductPage3 extends Component {
constructor(props) {
super(props);
this.state = {
slideCount: 1,
value: 'medium', cartData: {}
}
this.nextSlide = this.nextSlide.bind(this);
this.previousSlide = this.previousSlide.bind(this);
this.handleClick = this.handleClick.bind(this);
this.change = this.change.bind(this);
}
handleClick() {
let cart = {price:25,item:this.description.innerHTML,size:this.state.value};
this.props.onCartAdd(cart);
console.log(cart);
this.itemSelection(cart);
}
...
componentDidMount () {
window.scrollTo(0, 0)
}
render() {
return (
<div className= "ProductPage" id="ProductPage">
<div id='slider'>
{this.state.slideCount === 1 ? <Slideone /> : null}
{this.state.slideCount === 2 ? <Slidetwo /> : null}
<RightArrow nextSlide={this.nextSlide} />
<LeftArrow previousSlide={this.previousSlide} />
</div>
<div id='InfoSquare'>
<div id='wrapper'>
<div id='item' ref={i=>this.description=i}>LOGO TEE</div>
<div id='description'>Black tee 100% cotton with red silkscreened logo on front and back.</div>
<select id="size2" onChange={this.change} value={this.state.value}>
<option value="medium">Medium</option>
<option value="large">Large</option>
<option value="x-large">X-large</option>
</select>
<button onClick={this.handleClick} className="addit">ADD TO CART</button>
</div>
</div>
</div>
);
}
nextSlide() {
this.setState({ slideCount: this.state.slideCount + 1 })
}
previousSlide() {
this.setState({ slideCount: this.state.slideCount - 1 })
}
}
const mapDispatchToProps = (dispatch) => {
return {
onCartAdd: (cart) => {
dispatch(addCart(cart));
},
}
}
function mapStateToProps(state) {
return {
cart: state.cart
};
}
export default connect(mapStateToProps,mapDispatchToProps)(ProductPage3);
This is my Cart component:
import React, { Component } from 'react';
import {addCart} from './Shop';
import { removeCart } from '../../actions';
import { connect } from 'react-redux';
export class Cart extends Component {
constructor(props) {
super(props);
this.state = {items: this.props.cart,cart: [],total: 0};
}
...
render() {
return(
<div className= "Webcart" id="Webcart">
<div id='WebcartWrapper'>
<ul id='webCartList'>
{this.state.items.map((item, index) => {
return <li className='cartItems' key={'cartItems_'+index}>
<h4>{item.item}</h4>
<p>Size: {item.size}</p>
<p>Price: {item.price}</p>
<button onClick={() => this.handleClick(item)}>Remove</button>
</li>
})}
</ul>
<div>Total: ${this.countTotal()}</div>
</div>
</div>
);
}
}
const mapDispatchToProps = (dispatch) => {
return {
onCartAdd: (cart) => {
dispatch(addCart(cart));
},
onCartRemove: (item) => {
dispatch(removeCart(item));
},
}
}
function mapStateToProps(state) {
return { cart: state.cart };
}
export default connect(mapStateToProps, mapDispatchToProps)(Cart);
In Cart I'm rendering the item selection data for each object added to the cart. Here is where I want to display the item image also.
Since I have a image slider set up, an example of one of the slides would be:
import React, { Component } from 'react';
import take1 from './DETAIL.png';
const SlideNocHOne= (props) => {
return <img src= {take1} id="slide"></img>
}
export default SlideNocHOne;
Let's say I want this DETAIL.png image on the Cart, how could I transfer it with the user selection using Redux?
These are my Redux components:
import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import reducer from './reducers';
import thunkMiddleware from 'redux-thunk';
import {createLogger} from 'redux-logger';
const store = createStore(
reducer,
undefined,
compose(
applyMiddleware(createLogger(), thunkMiddleware),
autoRehydrate()
)
);
persistStore(store, {whitelist: ['cart']});
export default store;
import {ADD_CART} from './actions';
import {REMOVE_CART} from './actions';
import { REHYDRATE } from 'redux-persist/constants';
export default Reducer;
var initialState = {
cart:{},
data: [],
url: "/api/comments",
pollInterval: 2000
};
function Reducer(state = initialState, action){
switch(action.type){
case REHYDRATE:
if (action.payload && action.payload.cart) {
return { ...state, ...action.payload.cart };
}
return state;
case ADD_CART:
return {
...state,
cart: [...state.cart, action.payload]
}
case REMOVE_CART:
return {
...state,
cart: state.cart.filter((item) => action.payload !== item)
}
default:
return state;
};
}
export const ADD_CART = 'ADD_CART';
export const REMOVE_CART = 'REMOVE_CART';
export function addCart(item){
return {
type: ADD_CART,
payload: item
}
};
export function removeCart(item){
return{
type:REMOVE_CART,
payload: item
}
};
How can I use my Redux setup to transfer the image of a user selection to Cart?
If the path's of your components are relatively stable and you have a single location for the images, you can simply have a function that takes a component's displayName (in your example, Cart, etc.) and returns the relative path the image dir.
If you have that, you can just save a key/value collection in the reducer for what images each component should have, like:
{
CartComponent: ['DETAIL.png', 'DETAIL_2.png']
...
}
When rending just use the mapper function which will provide you a relative path and that's it. Something like (or you can just map out that array):
const relativeImagePath = getRelativeImageDirPathByCompName('CartComponent') + this.props.images.CartComponent[0];
Use require to fetch the image in the template like:
<img src={require(relativeImagePath)} alt="Something"/>
Hi All I am new to redux. I am creating a sample app as below:
entry point: index.js
import 'babel-polyfill'
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import inboundApp from './reducers'
import App from './components/App'
let store = createStore(inboundApp)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
)
/components/App.js
import React from 'react'
import HeaderContainer from '../containers/HeaderContainer'
import LoginForm from '../containers/LoginForm'
const App = () => (
<div>
<HeaderContainer />
<LoginForm />
</div>
)
export default App
/containers/LoginForm.js
import React from 'react'
import { connect } from 'react-redux'
import { login } from '../actions'
let LoginForm = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(login(input.value))
input.value = ''
}}>
<input ref={node => {
input = node
}} />
<button type="submit">
Login
</button>
</form>
</div>
)
}
LoginForm = connect()(LoginForm)
export default LoginForm
/actions/index.js
export const login = (supplierId) => {
return {
type: 'LOGIN',
supplierId
}
}
/containers/HeaderContainer.js
import { connect } from 'react-redux'
import Header from '../components/Header'
const mapStateToProps = (state) => {
return {
supplierId: state.supplierId
}
}
const HeaderContainer = connect(
mapStateToProps,
null
)(Header)
export default HeaderContainer
/components/Header.js
import React, { PropTypes } from 'react'
const Header = ({ supplierId}) => {
return (
<div>
<span>Your Supplier ID: </span> {supplierId}
</div>
)
}
export default Header
/reducers/loginForm.js
const loginForm = (state = '', action) => {
switch (action.type) {
case 'LOGIN':
return Object.assign({}, state, {
supplierId: action.supplierId
})
default:
return state;
}
}
export default loginForm
/reducers/index.js
import { combineReducers } from 'redux'
import loginForm from './loginForm'
const inboundApp = combineReducers({
loginForm
})
export default inboundApp
The problem is my presentation component Header does not get update by the action LOGIN which is firing by click on the button in the LoginForm.js.
would you please help me to find what am I missing? what's wrong with this code?
thanks
I think you try to get the supplierId, from the wrong namespace and your default state of loginForm is not good. Try like that:
const loginForm = (state = {supplierId: ''}, action) => {
switch (action.type) {
case 'LOGIN':
return Object.assign({}, state, {
supplierId: action.supplierId
})
default:
return state;
}
}
And the connect
const mapStateToProps = (state) => {
return {
supplierId: state.loginForm.supplierId
}
}