React Redux: how do you access response of post in component? - javascript

I'm working on a project based on this: https://github.com/bradtraversy/lead_manager_react_django
I need to access the pk of a newly created object. I can console.log the values I need in the action but cannot figure out how to use it in the component. Redux tools shows the new values as well in the diff.
What am I missing?
Action:
export const addPlan = (plan) => (dispatch, getState) => {
axios.post("/api/plans/", plan, tokenConfig(getState))
.then(res => {
dispatch(createMessage({ createPlan: 'Plan created.'}));
dispatch({
type: ADD_PLAN,
payload: res.data
});
}).catch(err => dispatch(returnErrors(err.response.data, err.response.status)));
Reducer:
case ADD_PLAN:
return {
...state,
plans: [...state.plans, action.payload]
};
mapStateToProps in component:
const mapStateToProps = state => ({
plans: state.plans.plans,
production: state.plans.production
});

You need to use mapStateToProps with connect inside the component.
function mapStateToProps(state) {
const { plans } = state
return { todoList }
}
export default connect(mapStateToProps)(YourComponent)
You can read more about connecting your component with redux store here

Turns out, everything was setup correctly but I just didn't know where to get access to the new props. I used componentWillReceiveUpdate and a little logic to prevent default and it works like a charm.

Related

React-redux component not re-rendering on store props change

My react component is not re-rendering despite its props being updated and I don't understand why.
Here's my component
import { fetchLocations } from 'state/locations/actions';
class Event extends React.Component {
componentDidMount() {
this.props.fetchLocations();
}
render() {
const { locations } = this.props;
return <span>{locations.map((l) => {return <span>{l}</span>;})}</span>;
}
}
const mapStateToProps = (state) => ({
locations: state.locations
})
export default connect(
mapStateToProps,
{ fetchLocations },
)(Event);
Here is my locations action file
export const fetchLocations = () = (dispatch) => {
axios.get('/api/locations')
.then(response => {
const locations = response.data;
dispatch({ type: FETCH_LOCATIONS_SUCCESS, payload: locations });
});
}
And my entities reducer
function entities(state = { locations: {} }, action) {
switch (action.type) {
case FETCH_LOCATIONS_SUCCESS:
return Object.assign({}, state, {
locations: action.payload
})
default:
return state
}
}
After this, my Event component should re-render. It doesn't. Using the react dev tools chrome extension I see that the locations are indeed there as props, but they do not show on the UI.
If I unmount the component by going to a different page and re-mount it, the locations show up properly.
It looks like everything works fine except the re-render is not triggering. componentDidUpdate is never fired.
If I manually do a setTimeout to forceUpdate an arbitrary second later, they show up.
Why isn't my component re-rendering?
Please, try to add key prop to span element of the render method. locations.map((l,key)=> <span key={key} >{l} </span>

Infinite loop during useEffect and Reducer

I don't know why but I have infinite loop when fetching data in Redux operations.
I have an app with Redux and ReactJS.
This is my React component
const CustomersTable = (props) => {
useEffect( () => {
props.getAllCustomers()
}, []);
return <Table ...props.customers />
}
const mapStateToProps = (state) => ({
customers: state.customers,
})
const mapDispatchToProps = dispatch => ({
getAllCustomers: () => dispatch(getAllCustomers()),
})
export default connect(
mapStateToProps, mapDispatchToProps
)(CustomersTable);
This is getAllInvoices()
const fetchCustomers = async() => {
/**
* I fetch only documents with flag delete==false
*/
const snapshot = await firestore.collection("customers").where('deleted', '==', false).get()
let data = []
snapshot.forEach(doc => {
let d = doc.data();
d.id_db = doc.id
//...other
data.push(d)
})
return data
}
export const getAllCustomers = () =>
async (dispatch) => {
const customers = await fetchCustomers()
// I reset state becouse I wont duplicate inovices in tables
dispatch(actions.reset())
customers.map(customer => dispatch(
actions.fetch(customer)
))
}
And reducers
const customerReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.FETCH_CUSTOMERS:
return {
...state, list: [...state.list, action.item]
}
case types.RESET_CUSTOMERS:
return {
...state, list: []
}
default:
return state
}
}
I expect that reducers RESET_CUSTOMERS and then FETCH_CUSTOMERS done job. But it still working in loop reset->customers.
I thought that is still rendered the component in useEffect but I think that hook is writing good.
I tested other reducers which are copy-pase reducers from Customers and they work well.
EDIT 1
#godsenal, thanks for your reply:
actions.js:
import types from './types'
const fetch = item => ({
type: types.FETCH_CUSTOMERS, item
})
const reset = item => ({
type: types.RESET_CUSTOMERS, item
})
export default {
fetch,
reset
}
As regards <Table /> it is AntDesign component (https://ant.design/components/table/). Without that, it looks the same.
EDIT 2
It is incredible. I copied all files from modules (customers) and paste into contracts directory. Then I changed all variables, functions, etc from customer to contract. Now it working (only contracts), but customers infinite loop. Maybe something disturbs in outside a structure.
EDIT 3
I found in app.js that in mapStateToProps I added customers to props. After remove (because I don't need it in root component) it began works fine. I suspect that fetch method in <CustomerTable /> affect the <App /> component and it render in a loop. I discovered that component isn't still updated in a loop, but its mounts and unmounts in a loop.
But still, I don't understand one thing. In <App />, I still have in mapStateToProps dispatching invoice from a store (the same case as customers) and in this case, everything works fine.

extracting data using mapstatetoprops

I have this component where I need some data from my Redux store.
However, I see it has been passed some other required data in a bit different way. My concern is as how to use mapStateToProps in this case and get the data.
Here is the component where I need to extract data from redux store:
const NavBarScore = withStyles(navBarScoreStyles)(
({ classes, matchDetails }) => {
// some opeartions on matchDetails
return (
<span className={classes.middleScoreContainer}>
<span className={classes.teamName}>{scoreData.homeTeamName}</span>
<span className={classes.teamName}>{scoreData.awayTeamName} </span>
</span>
);
}
);
I see that in one of the component there is something like this, where CricketFantasy is in one of the rooteReducer:
const NavBarScore = connect(({ cricketFantasy: { matchDetails } }) => ({
matchDetails
}))(NavScore);
I tried doing similar thing in another component and accessing it but it does not show any data.
My concern is how to simply get data from redux in this component using mapstatetoprops.
Create a container component for NavScore say NavScoreContainer which will dispatch events to redux and fetches data and maps state to props.
For example:-
const mapStateToProps = (state, ownProps) => {
return ({
scores: state.scores
})
}
const mapDispatchToProps = (dispatch, ownProps) => ({
fetchScores: () => {
dispatch(fetchScores());
}
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(NavScore)
Here scores will be passed as a prop to the component and can be accessed as this.props.scores.
declare your component like this:
const NavBarScore = ({ classes, matchDetails }) => {
// some opeartions on matchDetails
return (
<span className={classes.middleScoreContainer}>
<span className={classes.teamName}>{scoreData.homeTeamName}</span>
<span className={classes.teamName}>{scoreData.awayTeamName} </span>
</span>
)
}
with the declaration of your NavBarScore component, just export it this way:
export default withStyles(navBarScoreStyles)(connect(({ cricketFantasy: { matchDetails } }) => ({ matchDetails: cricketFantasyMatchDetails }))(NavBarScore))
When you import it, it will get the data you need or assign it to a new variable if you only need it in the same file.
const NavBarScoreWithData = withStyles(navBarScoreStyles)(connect(({ cricketFantasy: { matchDetails } }) => ({ matchDetails: cricketFantasyMatchDetails }))(NavBarScore))
this is assuming that you have cricketFantasy.matchDetails in your global state, else you can review and test it with connect(state => state)

redux way of doing doesn't work for my sessionStorage

I am trying to learn redux.
I am trying to add favorites functionality through Redux.
so I created actions addFavoriteSPORTSs, reducers SPORTSReducer, and then dispatched in tab-demo.js where i am doing mapDispatchToProps and
mapStateToProps
when I click the heart icon I am adding favorites in session storage window.sessionStorage.setItem(
"favoriteValues",
JSON.stringify(action.payload)
);
but the problem is after the refresh the color is not staying in the heart.
I debugged in componentDidMount and I am able to print the favotites get item value but still colr not maintaining.
can you tell me how to fix it.
so that in future I will fix itmyself.
providing my code snippet below
https://codesandbox.io/s/5x02vjjlqp
actions/index.js
import {
ADD_SPORTS,
DELETE_SPORTS,
DELETE_ALL_SPORTS,
ADD_ALL_SPORTSS
} from "./types";
export const addFavoriteSPORTSs = data => ({
type: ADD_ALL_SPORTSS,
payload: data
});
actions/types.js
export const ADD_ALL_SPORTSS = "ADD_ALL_SPORTSS";
tab-demo.js
import { deleteAllPosts, addFavoriteSPORTSs } from "./actions/index";
componentDidMount() {
let favorites = window.sessionStorage.getItem("favoriteValues");
console.log("componentDidMount favorites--->", favorites);
if (favorites) {
this.props.addFavoriteSPORTSs(JSON.parse(favorites));
}
// debugger;
}
const mapDispatchToProps = dispatch => {
return {
onDeleteAllSPORTS: () => {
// console.log("called");
dispatch(deleteAllPosts());
},
addFavoriteSPORTSs: data => {
dispatch(addFavoriteSPORTSs(data));
}
};
};
const mapStateToProps = state => {
return {
SPORTSs: state.SPORTSs
};
};
export default withStyles(styles)(
connect(
mapStateToProps,
mapDispatchToProps
)(ScrollableTabsButtonForce)
);
SPORTSReducer.js
switch (action.type) {
case ADD_ALL_SPORTSS:
window.sessionStorage.setItem(
"favoriteValues",
JSON.stringify(action.payload)
);
return action.payload;
case ADD_SPORTS:
state = state.filter(comment => comment.id !== action.payload.id);
value = [...state, action.payload];
console.log("ADD_SPORTS state--->", state);
console.log("ADD_SPORTS value--->", value);
//return [...state, action.payload];
// state = state.filter(SPORTS => SPORTS.SPORTSID !== action.payload.SPORTSID);
// value = [...state, action.payload]
window.sessionStorage.setItem("favoriteValues", JSON.stringify(value));
console.log("JSON.stringify(value)--->", JSON.stringify(value));
console.log("state--->", state);
return state;
When the component mounts you retrieve your favourties and set the redux state via calling your prop method. Your component will receive this new state via mapStateToProps, but it won't update without a suitable lifecycle method like componentDidUpdate or componentWillReceiveProps.
You can check out the lifecycle methods here.
Also, you are mutating your state in redux which is something you want to avoid. See this line:
state = state.filter(comment => comment.id !== action.payload.id);
I would also recommend Redux middleware for these tasks. You can set up middleware that will write to session storage whenever a specific action occurs and you can then rehyrdate Redux from that as well.

How to get data from async functions in react components, react-redux?

I am new to redux and react. I have React container and component which gets data from the api request call. My question in basically, what is the best way to handle asyc functons of redux in react. I need help to get the data in react component.
Container.js: (incomplete, here I need help)
class Container extends React.Component {
state = {
userList: ''
}
componentDidMount() {
this.props.loadUserDetails();
}
render() {
return (
<div className="app">
<Component userList={this.state.userList}/>
</div>
);
}
}
const mapStateToProps = (state) => ({
userList: state.auth.userList
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
loadUserDetails
}, dispatch);
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(Container));
Componet.js: (Pure component, here I need to render the data)
class Component extends React.Component {
render() {
return (
<div className="component">
{this.props.userList}
</div>
);
}
}
modules/auth/index.js
export const loadUserDetails = () => {
return dispatch => {
dispatch({
type: types.LOAD_USER_REQUEST
});
request.get('/api/v1/auth', dispatch)
.then(({ data }) => {
if (data.success) {
dispatch({
type: types.LOAD_USER_SUCCESS
payload: data.data
});
} else {
dispatch({
type: types.LOAD_USER_FAILURE,
payload: data.message
});
}
})
.catch((err) => {
dispatch({
type: types.LOAD_USER_FAILURE,
payload: 'Something went wrong, please try again.'
});
});
};
};
modules/auth/actions.js:
export const LOAD_USER_REQUEST = 'auth/LOAD_USER_REQUEST';
export const LOAD_USER_SUCCESS = 'auth/LOAD_USER_SUCCESS';
export const LOAD_USER_FAILURE = 'auth/LOAD_USER_FAILURE';
modules/auth/reducers.js:
state ={
loading: false,
error: null,
userList: null
}
case types.LOAD_USER_REQUEST:
return Object.assign({}, state, {
loading: true
});
case types.LOAD_USER_REQUEST:
return Object.assign({}, state, {
loading: false,
userList: payload,
});
case types.LOAD_USER_REQUEST:
return Object.assign({}, state, {
loading: flase,
error: payload
});
I actually need help to get the userList in Container and pass it to the Component. because it's an asyc function I am not able to get the data in Container before Component renders. How to handle such situations?
As I am passing userList in Child component, for the first time I don't have the userList data. So my problem is with the cycles of Reactjs, Where should I call loadUserList ? In componentDidMount? Or componentDidUpdate? If so, how can I get the data?
I am not able to render the userList it's value is null when the Component mounts. How to solve this?
a good option is to make use of redux-saga, easy and simple to implement:
https://github.com/redux-saga/redux-saga

Categories

Resources