The state Selectedlan don't update on the click.. I think it is an error of reducer. Specifically when I the function handleGreeceLan is called the action should be called and the reducer but I think only the action is called
The file that I make the call
import { changeLanGR, changeLanEN } from '../actions/toolbarActions';
const handleGreeceLan = () => {
changeLanGR();
};
const handleUKLan = () => {
changeLanEN();
};
<MenuItem onClick={handleUKLan}>
...
</MenuItem>
<MenuItem onClick={handleGreeceLan}>
...
</MenuItem>
...
const mapStateToProps = state => {
return { Selectedlan: state.Selectedlan };
};
export default connect(
mapStateToProps,
{ changeLanGR, changeLanEN }
)(Language);
Reducer
import { combineReducers } from 'redux';
import { LAN_GR, LAN_EN } from '../actions/types';
const selectLanguage = (state = LAN_GR, action) => {
if (action.type === LAN_GR) {
return { ...state, Selectedlan: LAN_GR};
}
else if (action.type === LAN_EN) {
return { ...state, Selectedlan: LAN_EN};
}
return state;
};
export default combineReducers({
Selectedlan: selectLanguage
});
Action
import { LAN_GR, LAN_EN } from './types';
export const changeLanGR = () => {
return {
type: LAN_GR
};
};
export const changeLanEN = () => {
return {
type: LAN_EN
};
};
You are mapping the dispatch function to props, meaning you need to call those functions mapped from props:
const Language = ({
className,
changeLanGR,
changeLanEN,
...rest
}) => {
const handleGreeceLan = () => {
changeLanGR();
};
const handleUKLan = () => {
changeLanEN();
};
Related
When I use dispatch as follows in my react component, My component keeps rendering. How can I avoid that?
const dispatch = useDispatch();
useEffect(() => {
dispatch(reportsActionCreators.changeSalesDashboardData(someData));
}, []);
in the parent component, I'm using useSelector as this. But didn't use this report's data.
const { selectedSalesTab } = useSelector<RootState, any>((state: RootState) => {
return {
selectedSalesTab: state.reports.selectedSalesTab,
};
this is the parent component I'm using.
const SalesReports: FC = () => {
const dispatch = useDispatch();
const { selectedSalesTab } = useSelector<RootState, any>((state: RootState) => {
return {
selectedSalesTab: state.reports.selectedSalesTab,
};
});
const getPageContent = useMemo(() => {
switch (selectedSalesTab) {
case salesReportsTabs[0].id:
return <Dashboard />;
default:
return <div>not found :(</div>;
}
}, [selectedSalesTab]);
return (
<div className="sales-report-wrapper">
<GTTabs
id="sales-reports-tabs"
onClickTab={(tab: Tab) => dispatch(reportsActionCreators.changeSalesTab(tab.id))}
tabs={salesReportsTabs}
defaultSelectedTabId={selectedSalesTab}
/>
<div>{getPageContent}</div>
</div>
);
};
export default SalesReports;
this is the Child component I'm using
const Dashboard: FC = () => {
const repostsRxjs = rxjsConfig(reportingAxios);
const dispatch = useDispatch();
useEffect(() => {
repostsRxjs
.post<SalesDashboardItem[]>(
'/sales-data/order-details/6087bc3606ff073930a10848?timezone=Asia/Dubai&from=2022-09-03T00:00:00.00Z&to=2022-12-25T00:00:00.00Z&size=10',
{
brandIds: [],
channelIds: [],
kitchenIds: [],
countryIds: [],
},
)
.pipe(
take(1),
catchError((err: any) => of(console.log(err))),
)
.subscribe((response: SalesDashboardItem[] | void) => {
if (response) {
dispatch(reportsActionCreators.changeSalesDashboardData(response));
}
});
}, []);
const { isActiveFilter } = useSelector<RootState, any>((state: RootState) => {
return {
isActiveFilter: state.filterData.isActiveFilter,
};
});
return (
<>
<div
onClick={() => {
dispatch(filterssActionCreators.handleFilterPanel(!isActiveFilter));
dispatch(
filterssActionCreators.changeSelectedFiltersType([
FilterTypes.BRAND,
FilterTypes.CHANNEL,
FilterTypes.COUNTRY,
FilterTypes.KITCHEN,
]),
);
}}
>
Dashboard
</div>
{isActiveFilter && <FilterPanel />}
</>
);
};
export default Dashboard;
Actions
import { SalesDashboardItem } from 'app/models/Reports';
import { actionCreator } from 'app/state/common';
export type ChangeSalesTabPayload = string;
export type ChangeSalesDashboardDataPayload = SalesDashboardItem[];
export const reportsActionTypes = {
CHANGE_SALES_TAB: 'CHANGE_SALES_TAB',
CHANGE_SALES_DASHABOARD_DATA: 'CHANGE_SALES_DASHABOARD_DATA',
};
export const reportsActionCreators = {
changeSalesTab: actionCreator<ChangeSalesTabPayload>(reportsActionTypes.CHANGE_SALES_TAB),
changeSalesDashboardData: actionCreator<ChangeSalesDashboardDataPayload>(
reportsActionTypes.CHANGE_SALES_DASHABOARD_DATA,
),
};
export type ReportsAction = {
type: typeof reportsActionTypes.CHANGE_SALES_TAB | typeof reportsActionTypes.CHANGE_SALES_DASHABOARD_DATA;
payload: ChangeSalesTabPayload | ChangeSalesDashboardDataPayload;
};
Reducer
import { SalesDashboardItem } from 'app/models/Reports';
import { salesReportsTabs } from 'app/utils/reports';
import { reportsActionTypes, ReportsAction } from './actions';
export type ReportsState = {
selectedSalesTab: string;
salesDashboardFilterData: {
brands: string[];
kitchens: string[];
channels: string[];
countries: string[];
};
salesDashBoardDatta: SalesDashboardItem[];
};
const initialState: ReportsState = {
selectedSalesTab: salesReportsTabs[0].id,
salesDashboardFilterData: {
brands: [],
kitchens: [],
channels: [],
countries: [],
},
salesDashBoardDatta: [],
};
export default (state = initialState, action: ReportsAction): ReportsState => {
switch (action.type) {
case reportsActionTypes.CHANGE_SALES_TAB:
return { ...state, selectedSalesTab: action.payload as string };
case reportsActionTypes.CHANGE_SALES_DASHABOARD_DATA:
return { ...state, salesDashBoardDatta: action.payload as SalesDashboardItem[] };
default:
return state;
}
};
root reducer
import { combineReducers } from 'redux';
import SidePanelReducer from './reducers/sidepanel.reducer';
import authReducer from './auth';
import onboardingReducer from './onboarding';
import applicationReducer from './application';
import inventoryConfigReducer from './inventoryConfig/inventory.reducer';
import reportsReducer from './reports';
import filtersReducer from './filter';
const rootReducer = combineReducers({
sidePanel: SidePanelReducer,
auth: authReducer,
onboarding: onboardingReducer,
application: applicationReducer,
inventory: inventoryConfigReducer,
reports: reportsReducer,
filterData: filtersReducer,
});
export default rootReducer;
when I'm adding the dispatch action in useEffect(componentDidMount) this looping is happening. Otherwise, this code works fine. How can I avoid that component rerendering?
I think the issue is that the useSelector hook is returning a new object reference each time which triggers the useMemo hook to re-memoize an "instance" of the Dashboard component. The new "instance" of Dashboard then mounts and runs its useEffect hook which dispatches an action that updates the state.reports state in the Redux store.
Instead of creating and returning a new object reference to destructure selectedSalesTab from, just return the state.reports object directly.
Change
const { selectedSalesTab } = useSelector<RootState, any>((state: RootState) => {
return {
selectedSalesTab: state.reports.selectedSalesTab,
};
});
to
const { selectedSalesTab } = useSelector<RootState, any>((state: RootState) => {
return state.reports;
});
I'm a little confused on passing an object to the redux store. I have successfully created the store and can add items from the initial state. The function also fires when called
Action:
import { GET_ITEM } from './OrderTypes'
export const getItem = (payload) => {
return {
type: GET_ITEM,
payload: { payload }
}
}
Reducer:
import { GET_ITEM } from './OrderTypes'
const initialState = {
orderList: [],
}
const orderReducer = (state = initialState, action) => {
switch (action.type) {
case GET_ITEM: return {
...state,
orderList: [...state.orderList, action.payload]
}
default: return state
}
}
export default orderReducer
Component:
class TestComponentextends Component {
pushItem = () => {
this.props.getItem({
payload: 'test object'
})
}
render() {
return (
<input type='button' value='test btn' onClick={this.pushItem} />
)
}
}
const mapStateToProps = state => {
return {
orderList: state.orderList
}
}
const mapDispatchToProps = dispatch => {
return {
getItem: () => dispatch(getItem())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(TestComponent)
What happens: An empty object is added to the orderList array.
What I want to happen: Store the object in pushItem in the orderList array.
Your mapDispatchToProps doesn't pass the arguments to the action creator (see mapDispatchToProps function arguments - 2nd example):
const mapDispatchToProps = dispatch => ({
getItem: (...args) => dispatch(getItem(...args))
})
Even easier is to let react-redux handle the mapping by using mapDispatchToProps as an object:
const mapDispatchToProps = {
getItem
}
I created an action creator that is simply supposed to make a get request to my API and return with a list of all projects. However, for some reason, my return dispatch in my thunk function is not firing at all. It gets to the console.log() statement and just ends. There are no consoles errors, and no network calls being made either as far as I can tell. Any ideas why it would do absolutely nothing?
Dashboard.js (component)
import ProjectItem from "../Project/ProjectItem";
import styles from "./Dashboard.module.css";
import CreateProjectButton from "../CreateProjectButton/CreateProjectButton";
import { connect } from "react-redux";
import { getProjects } from "../../Redux/getProjects/actions";
const Dashboard = props => {
useEffect(() => {
console.log("blah");
getProjects();
}, []);
return (
<div className={styles.dashboardContainer}>
<h1>Projects</h1>
<br />
<CreateProjectButton />
<br />
<hr />
<ProjectItem />
</div>
);
};
const mapStateToProps = state => {
return {
projects: state
};
};
const mapDispatchToProps = dispatch => {
return {
getProjects: () => dispatch(getProjects())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
action.js (action creator)
import { GET_PROJECTS_SUCCESS, GET_PROJECTS_ERROR } from "./constants";
export const getProjectsSuccess = payload => {
console.log("getProjectSuccess", payload);
return {
type: GET_PROJECTS_SUCCESS,
payload
};
};
export const getProjectsError = () => {
console.log("there was an error");
return {
type: GET_PROJECTS_ERROR
};
};
export function getProjects() {
console.log("getProject");
return dispatch => {
axios
.get("/project/all")
.then(res => dispatch(getProjectsSuccess(res.data)))
.catch(err => dispatch(getProjectsError(err)));
};
}
index.js (getProject reducer)
const initialState = {
projects: [], //array of projects
project: {}, // single project for update case
reRender: false
};
const getProjectsReducer = (state = initialState, action) => {
switch (action.type) {
case GET_PROJECTS_SUCCESS:
return { ...state, projects: action.payload }; // will need to change action.payload later on
default:
return state;
}
};
export default getProjectsReducer;
constants.js
export const GET_PROJECTS_SUCCESS = "GET_PROJECTS_SUCCESS";
export const GET_PROJECTS_ERROR = "GET_PROJECTS_ERROR";
rootReducer.js
import createProjectReducer from "./createProject/index";
import getProjectsReducer from "./getProjects/index";
const rootReducer = (state = {}, action) => {
return {
project: createProjectReducer(state.project, action),
projects: getProjectsReducer(state.projects, action)
};
};
export default rootReducer;
FIXED: After reading up on the use effect hook in functional components I realized I was missing props.getProjects in the useEffect function in dashboard.js!
I'm using react and redux and I'm trying a simple example.
If I clicked the button, then the number should increment by 1.
But when I currently click the button you'll see all element gonna + 1
how can i fix that?...
class Menu extends Component {
componentDidMount() {
this.props.getItems();
}
plus = () =>{
this.props.getplus();
}
render() {
const {item, count} = this.props.item
return (
<div>
{item.map(items => {
<div>{items.example}</div> <buttom onClick={this.plus}> + </button>
<div>{count}
</div> }
</div>
) }
const mapStateToProps = (state) => ({
item: state.item
})
export default connect(mapStateToProps , { getItems, getplus }) (Menu);
itemAction.js
export const getItems = () =>{
return {
type: GET_ITEMS
} }
export const getplus = () => {
return {
type: PLUS_ITEMS
} }
Reducer.js
const initialState = {
item: [
{
example:"example1"
},
{
example:"example2"
},
{
example:"example3"
},
],
count:0
}
export default function (state = initialState, action) {
switch(action.type){
case GET_ITEMS:
return {
...state
}
case PLUS_ITEMS:
return {
...state,
count:state.count + 1
}
default:
return state;
}
}
I see two errors in your code, replace this:
const {item, count} = this.props.item
with this:
const {item, count} = this.props
And map also count to your props:
const mapStateToProps = (state) => ({
item: state.item
count: state.count
})
This is my component:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { Divider } from "antd";
import MovieList from "../components/MovieList";
import IncreaseCountButton from "../components/IncreaseCountButton";
import DeleteButton from "../components/DeleteButton";
import { deleteMovie, increaseCount } from "../actions/movies";
import { getIsDeleting, getIsIncreasing } from "../reducers/actions";
export class MovieListContainer extends Component {
constructor(props) {
super(props);
this.handleIncrease = this.handleIncrease.bind(this);
this.handleDelete = this.handleDelete.bind(this);
}
static propTypes = {
isIncreasing: PropTypes.func.isRequired,
isDeleting: PropTypes.func.isRequired,
};
async handleIncrease(movie) {
await this.props.increaseCount(movie, this.props.token);
}
async handleDelete(movie) {
await this.props.deleteMovie(movie.id, this.props.token);
}
render() {
return (
<MovieList movies={this.props.movies}>
{(text, movie) => (
<div>
<IncreaseCountButton
onIncrease={() => this.handleIncrease(movie)}
loading={this.props.isIncreasing(movie.id)}
/>
<Divider type="vertical" />
<DeleteButton
onDelete={() => this.handleDelete(movie)}
loading={this.props.isDeleting(movie.id)}
/>
</div>
)}
</MovieList>
);
}
}
export const mapStateToProps = state => ({
isIncreasing: id => getIsIncreasing(state, id),
isDeleting: id => getIsDeleting(state, id),
});
export default connect(
mapStateToProps,
{ deleteMovie, increaseCount }
)(MovieListContainer);
I feel like this might be bad for performance/reconciliation reasons, but not sure how else to retrieve the state in a way that hides implementation details.
Gist link: https://gist.github.com/vitalicwow/140c06a52dd9e2e062b2917f5c741727
Any help is appreciated.
Here is how you can handle these asynchronous actions with redux. You can use thunk to perform 2 actions and can store a flag to determine what is being done to an object (Deleting, Changing, etc):
action
export const deleteMovieAction = id => {
return dispatch => {
dispatch({ type: "MOVIE_DELETING", id });
setTimeout(() => {
dispatch({ type: "MOVIE_DELETED", id });
}, 2000);
};
};
reducer
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case "MOVIE_DELETING": {
const movies = [...state.movies];
movies.find(x => x.id === action.id).isDeleting = true;
return { ...state, movies };
}
case "MOVIE_DELETED": {
const movies = state.movies.filter(x => x.id !== action.id);
return { ...state, movies };
}
default:
return state;
}
};
https://codesandbox.io/s/k3jnv01ymv
An alternative is to separate out the ids into a new array that are being deleted
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case "MOVIE_DELETING": {
const movieDeletingIds = [...state.movieDeletingIds, action.id];
return { ...state, movieDeletingIds };
}
case "MOVIE_DELETED": {
const movieDeletingIds = state.movieDeletingIds.filter(
x => x.id !== action.id
);
const movies = state.movies.filter(x => x.id !== action.id);
return { ...state, movieDeletingIds, movies };
}
default:
return state;
}
};
https://codesandbox.io/s/mj52w4y3zj
(This code should be cleaned up, but is just to demo using thunk)