Is this the right way to pass state to Redux action? - reactjs

I'm using map to get the IDs from the array of objects in the render.
My code:
class AppliedCandidates extends Component {
render() {
const {
appliedjobs
} = this.props
const {
joblists
} = this.props {
joblists && joblists.map(joblist => {
this.props.getAppliedJobs(joblist.id)
})
}
return ( <
div > {
appliedjobs && appliedjobs.map(appliedjob => {
return <ol >
<
li > {
appliedjob.jobid
} < /li> <
li > {
appliedjob.candidatephoneno
} < /li> <
/ol>
})
} <
/div>
);
}
}
const mapStateToProps = (state) => {
console.log("state", state);
return {
joblists: state.getJobs.job,
appliedjobs: state.getAppliedJobs.appliedjob
}
}
const mapDispatchToProps = (dispatch) => {
return {
getAppliedJobs: (joblists) => dispatch(getAppliedJobs(joblists))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AppliedCandidates);
and in the following code which is in the Redux action.js, if I uncomment the array variable "appliedjobs" the process goes into an endless loop. If I comment it out, I only get the last value.
var appliedjobs = []
const getAppliedJobs = (joblists) => {
return (dispatch, getState, {
getFirebase,
getFirestore
}) => {
const firestore = getFirestore();
firestore.collection('Jobs').doc(joblists).collection('AppliedJobs').where("jobid", "==", joblists)
.get()
.then((querySnapshot) => {
if (querySnapshot.empty === true) {
dispatch({
type: 'GET_APPLIED_JOB_ERROR',
joblists
});
} else {
//appliedjobs =[]
querySnapshot.forEach(function(doc3) {
appliedjobs.push({
candidatephoneno: doc3.data().candidatephoneno,
jobid: doc3.data().jobid,
});
});
dispatch({
type: 'GET_APPLIED_JOB',
payload: appliedjobs
});
}
})
}
};
How to get the values?

You shouldn't dispatch actions in your render function. If you need to populate your data by dispatching actions to the store, you should do it in a lifecycle method, in this case I think componentDidUpdate fits best.
from facebook's documentation:
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component has been updated. This is also a good place to do network requests as long as you compare the current props to previous props (e.g. a network request may not be necessary if the props have not changed).

As Dor Shinar has said, don't call actions on the render function. Why? Because render function will be invoked every time any props/state is updated. So, if you call the action there, you'll keep re-rendering your page since you'll keep getting new updated props from the dispatched action.
I'm not sure about your action, since I never use firebase. But I guess it's some query calls.
class AppliedCandidates extends Component {
componentDidMount() {
// here, the joblists props will get automatically updates from the dispatched redux action, dont mutate/change the props by yourself, change it via action.
const { getAppliedJobs, joblists } = this.props;
getAppliedJobs(joblists); // to be honest, why your action have the input of array and you put up id of individual array item? So I just put the entire joblists as the function parameter.
}
render() {
const { appliedjobs } = this.props;
if (appliedjobs.length === 0) {
return null;
}
return ( // you can add more
<ol>
{appliedjobs.map(appliedjob => <li>{appliedjob.id}</li>)}
</ol>
)
}
const mapStateToProps = (state) => {
return {
joblists: state.getJobs.job,
appliedjobs: state.getAppliedJobs.appliedjob
}
}
const mapDispatchToProps = (dispatch) => {
return {
getAppliedJobs : (joblists) => dispatch(getAppliedJobs(joblists))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AppliedCandidates);

Related

Redux Thunk Submit to Action

I been stuck to this quite a bit, I am trying to pass in my state to the redux but it seems like I am doing it wrong.
This are my code:
This is my submit function
popForm() {
let states = this.state.orders;
let d = states.filter((data) => {
return data !== null && data !== undefined
});
// console.log("d",d);
this.props.LogInClick(d);
// LogInClick(state);
}
This is my mapToDispatch
const mapDispatchToProps = (dispatch) => {
return {
LogInClick : (data) => dispatch(Actions.addDynamic(data)),
}
}
Action call
export const addDynamic = ({data}) => {
console.log("Manage to get to here");
console.log("dataInAction",data);
}
My reducer
case Actions.ADD_DYNAMIC: {
return {
...state,
data: action.payload
};
}
Your synchronous action should to return an object with type and payload.
When dealing with async actions, you need thunk(or saga etc) middleware. Your code seem to dispatch normal action (not async). So just make sure that your action returns type and payload.
Like this
export const addDynamic = ({data}) => {
console.log("Manage to get to here");
console.log("dataInAction",data);
return {
type: Action.ADD_DYNAMIC,
payload: data
}
}

component not re rendering on props change redux react

My component with filters doesn't re-render after changes in Redux state. With console.log() I can see that action and reducer works. ObjectFilter.js after changes gives good result with console, but doesn't re-render.
mapReducer.js
const mapReducer = (state = initState, action) => {
switch(action.type) {
case actions.SET_FILTERS:
console.log('SET_FILTERS', state)
return({
...state,
filters: action.filters
})
default:
return state;
}
}
export default mapReducer;
mapActions.js
export const setFilters = (el, old_filters) => {
let filters = old_filters;
let new_el = !old_filters[el];
filters[el] = new_el;
console.log(filters)
return (dispatch, getState) => {
dispatch({
type:actions.SET_FILTERS,
filters: filters
})
}
}
objectFilters.js
class ObjectFilters extends Component {
changeFilterHandler = (el) => {
this.props.setFilters(el, this.props.filters);
}
render () {
console.log(this.props.filters)
return (
/* some code */
);}
}
const mapDispatchToProps = dispatch => {
return {
setFilters: (el, filters) => dispatch(setFilters(el, filters))
}
}
const mapStateToProps = state => {
return {
filters: state.mapRedux.filters
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ObjectFilters);
The problem in your code is that you are mutating the old_filters directly, instead make a clone of it and then update filter value. Never mutate state and prop directly
export const setFilters = (el, old_filters) => {
let filters = {...old_filters}; // using spread operator to create a clone
let new_el = !old_filters[el];
filters[el] = new_el;
return (dispatch, getState) => {
dispatch({
type:actions.SET_FILTERS,
filters: filters
})
}
}

The action always dispatch and it does not stop, it runs infinitely

I have question about dispatch action. I do not know why my dispatch redux run infinitely.
Below is my ListUser component
import { ListUsersAction } from "../actions/ListUsersAction";
const ListUsers = props => {
var resPerPage = configList.users.resPerPage;
props.ListUsersAction(resPerPage, 1);
if (props.listUsersReducer.thanhvien.length > 0) {
const listUsersReducer = props.listUsersReducer;
const propToSend = {
currentPage: listUsersReducer.currentPage,
pages: listUsersReducer.pages,
resPerPage: listUsersReducer.resPerPage
};
return (
<Fragment>
<Pagination pageProp={propToSend} />
</Fragment>
);
} else {
return null;
}
};
const mapStateToProp = state => ({
listUsersReducer: state.listUsersReducer
});
export default connect(mapStateToProp, { ListUsersAction })(ListUsers);
and here is ListUserAction
export const ListUsersAction = (resPerPage, currentPage) => async dispatch => {
if (localStorage.token) {
setAuthToken(localStorage.token);
}
try {
const res = await axios.get('/api/admin/users/page/:page', {
params: {
page: currentPage,
resPerPage: resPerPage
}
});
dispatch({
type: LOADUSERS,
payload: res.data
});
}catch(err){
console.log(err);
dispatch({
type: STOPLOADUSERS
})
}
}
You can see the action always render
Can you tell me why and how to fix it?
You are calling your action every time your Component re renders, and calling your action is causing your Component to re render, creating an infinite loop.
Put your action inside a useEffect to prevent this and only call it once on component mount or whenever you want based on the dependency array:
useEffect(() => {
var resPerPage = configList.users.resPerPage;
props.ListUsersAction(resPerPage, 1);
},[])
const ListUsers = props => {
React.useEffect(()=>{
var resPerPage = configList.users.resPerPage;
props.ListUsersAction(resPerPage, 1);
},[])
// your code
};
try this
functional component render every times,
thats why it happend
check hooks API useEffect

How to get props at the first rendering

I have a container component in which I get the ID and drop this ID into the function and the request goes, in principle, the props should come right away, but they are undefined. But when you re-enter the same component, the necessary props are shown.
Explain how to make props appear on the first render?
class View extends React.Component {
componentDidMount() {
let id = this.props.match.params.id;
this.props.GetProjData(id);
}
render() {
return <ProjView {...this.props}></ProjView>;
}
}
let mapStateToProps = state => {
return {
initialValues: {
NameProj: state.project.OneProject.NameProj,
Text: state.project.OneProject.Text,
target: state.project.OneProject.target,
startdate: state.project.OneProject.startdate,
enddate: state.project.OneProject.enddate
},
error: state.settings.error,
loading: state.settings.loading
};
};
My request
export const GetProjData = data => async (
dispatch,
getState,
{ getFirestore }
) => {
const firestore=getFirestore()
try {
await firestore
.collection("Projects")
.where("idProject", "==", data)
.get().then(snap => {
snap.forEach(doc => {
let project=doc.data()
console.log(doc.data());
dispatch({type:getOne,project})
});
})
} catch (err) {}
};
If I'm understanding the flow of your app correctly, you need to account for the renders between when you request your project data and when you receive the project data.
class View extends React.Component {
// constructor fires first so we might as well move it here
constructor(props) {
const id = props.match.params.id;
props.GetProjData(id);
}
render() {
// Your component will rerender before receiving the new data.
// We block the component from mounting so that initialValues
// gets set only when we have the data needed
if (this.props.initialValues && this.props.initialValues.NameProj) {
// A better way to do this would be to listen to a loading variable
// that gets updated when your request finishes
return <ProjView {...this.props} />;
}
return null; // or loading graphic
}
}

Saving realtime listener in redux

I need to trigger firestore realtime listener on login to listen to user profile data changes and cancel it before logout. To do that I need to save realtime listener in the store where I get stuck. I'm trying to do this in redux
export const cancelListener = (cancelListener) => {
return {
type: actionTypes.CANCEL_LISTENER,
cancelListener: cancelListener
}
}
export const uDataListener = (uid) => {
return dispatch => {
dispatch(uDataStart())
const dbRef = db.collection("user").doc(uid)
const cancelSubscription = dbRef
.onSnapshot(
(doc) => {
dispatch(uDataSuccess(doc.data()))
}
, ((error) => {
dispatch(uDataFail(error.message))})
);
dispatch(cancelListener(cancelSubscription))
}
}
and on logout simply call it from the redux store
export const logout = (cancelListener) => {
cancelListener()
fire.auth().signOut()
return {
type: actionTypes.AUTH_LOGOUT
}
}
However nothing is being saved in cancelListener therefore it can not be triggered. How do I accomplish this task? Please
Thanks
I have woken up in the middle of the night with other idea. I tried to add the method in the constant in action instead of saving the method in the redux state or reducer. I'm not sure if this is the best approach but it does the job. Now I just don't understand why I didn't try this approach in the first place. Here is the code which will need a bit of tweaks yet but it works
let cancelListener = null
export const logout = () => {
cancelListener()
fire.auth().signOut()
return {
type: actionTypes.AUTH_LOGOUT
}
}
export const auth = (email, password) => {
return dispatch => {
dispatch(authStart())
fire.auth().signInWithEmailAndPassword(email, password).then((u) => {
dispatch(authSuccess(u.user))
const dbRef = db.collection("user").doc(u.user.uid)
cancelListener = dbRef.onSnapshot((doc) => {
dispatch(saveUserData(doc.data()))
})
}).catch((error) => {
dispatch(authFailed(error.message))
});
}
}
Thank you very much for your help anyway. I really appreciate that
Just a quick thought, in uDataListener call an action e.g. START_LISTENER and in reducer you can have:
import { store } from './yourStore';
let cancelListener, dbRef;
function reducer(state, action) {
switch (action.type) {
case "START_LISTENER":
dbRef = db.collection("user").doc(action.uid)
cancelSubscription = dbRef.onSnapshot(function(doc) {
store.dispatch(
yourAction(doc.data()); //Dispatch new action using store
)
})
return state;
case "STOP_LISTENER":
cancelListener()
return state;
default:
return state;
}
STOP_LISTENER will be dispached when you are doing logout
Below you can see link how to dispatch from outside a component
Update React component by dispatching action from non-react component

Resources