extracting data using mapstatetoprops - javascript

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)

Related

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

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.

How to reference a react component's state in a redux helper function

I'm new to redux, and I can find lots of info on how to pass a redux state to the component, but not the other way round, so I'm not sure if I'm searching the correct vocabulary. But Essentially I want to be able to reference the current state of a react component in a redux helper function, this is what I've done - and I'm getting TypeError: dispatch is not a function and handleSubmit is just launching as soon as the page is loaded:
App.js
render() {
return (
<div className="App">
<p>{this.state.id}</p>
<form onSubmit={this.handleSubmit(this.state.id)}>
<button type="submit">Submit</button>
</form>
</div>
)
}
const mapDispatchToProps = dispath => bindActionCreators({
handleSubmit
}, dispath);
export default connect(
mapDispatchToProps
)(App);
reducers.js
export const handleSubmit = (test) => {
window.open("http://localhost:5000/"+test);
}
//Reducer
export default (state = [], action) => {
switch (action.type) {
default:
return state;
}
};
First, you don't use the function that react-redux pass through the props and try to call handleSubmit on the component itself.
You are also calling the function inside onSubmit immediately instead of passing a reference to a function so wrap it in an arrow function and use handleSubmit from this.props
onSubmit={() => this.props.handleSubmit(this.state.id)}
Second, the first argument to connect is the mapping function to get a slice of the state called mapStateTpProps by convention, pass in null as the first argument.
there is also no need to use bindActionCreators and you can just pass an object with functions and react-redux will wrap them in dispatch for you
export default connect(
null,
{ handleSubmit }
)(App);
You need to put id to the state of App and manage it through redux.
Code below will help you.
// App.js
render() {
return (
<div className="App">
<p>{this.props.id}</p>
<form onSubmit={this.props.ActionSubmit(this.props.id)}>
<button type="submit">Submit</button>
</form>
</div>
)
}
const mapStateToProps = store => ({
id: store.appReducer.id,
})
const mapDispatchToProps = dispath => bindActionCreators({
ActionSubmit
}, dispath);
export default connect(
mapStateToProp,
mapDispatchToProps
)(App);
// reducers.js
export ACTION_SUBMIT = 'ACTION_SUBMIT'
export const ActionSubmit = id => ({
type: ACTION_SUBMIT,
payload: {
id,
}
})
const initialState = {
id: 0,
}
const doSubmit = (id) => {
window.open("http://localhost:5000/"+id);
}
export default AppReducer(state = initialState, action) {
switch(action.type) {
case ACTION_SUBMIT:
doSubmit( action.payload.id)
return {
id: action.payload.id,
}
default:
return state
}
}

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>

React useEffect hook - infinite loop with redux action that uses ID

I'm using useEffect in combination with reduct actions. I'm aware that I have to extract the action function from the props and provide it as the second argument which generally works for bulk fetches. But if I use an ID from the props as well, it ends up in an infinity loop:
export function EmployeeEdit(props) {
const { fetchOneEmployee } = props;
const id = props.match.params.id;
const [submitErrors, setSubmitErrors] = useState([]);
useEffect(() => fetchOneEmployee(id), [fetchOneEmployee, id]);
const onSubmit = employee => {
employee = prepareValuesForSubmission(employee);
props.updateEmployee(employee._id, employee)
.then( () => props.history.goBack() )
.catch( err => setSubmitErrors(extractErrors(err.response)) );
};
return (
<div>
<h3>Edit Employee</h3>
<NewEmployee employee={props.employee} employees={props.employees} onSubmit={onSubmit} submitErrors={submitErrors} />
</div>
)
}
EmployeeEdit.propTypes = {
fetchOneEmployee: PropTypes.func.isRequired,
employees: PropTypes.array.isRequired,
employee: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
employees: state.employees.items,
employee: state.employees.item
})
export default connect(mapStateToProps, { fetchOneEmployee, updateEmployee })(EmployeeEdit);
And the redux action:
export const fetchOneEmployee = (id) => dispatch => {
axios.get(`http://localhost:8888/api/users/${id}`)
.then(res => {
const employee = res.data;
dispatch({
type: FETCH_ONE_EMPLOYEE,
payload: employee
})
})
});
};
Anybody an idea what I'm doing wrong?
one of the values in your dependency array ([fetchOneEmployee, id]) is changing. It is hard to say which value it is with the limited code you have supplied.
At first glance though, you probably want fetchOne instead of fetchOneEmployee in your array.
Your inifite loop is probably caused because of fetchOneEmployee passed as argument to useEffect. Did you pass fetchOneEmployee to EmployeeEdit as arrow function? If you did then fetchOneEmployee always will be change.
This is a part of great article about react hooks fetch data.
https://www.robinwieruch.de/react-hooks-fetch-data
I especially recommended header CUSTOM DATA FETCHING HOOK

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.

Categories

Resources