action method not getting called - reactjs

I am trying to get a simple react-redux app to work and I am running into a weird error that I can't figure out. I am trying to simply update my current user's status and handle the store and it doesnt work. changeUserStatus is not getting called
setCurrentUserFirstName - works setCurrentUserHandle - doesn't
export const changeUserStatus = (userid,status) => async (dispatch, getState, { getFirebase }) => {
const firebase = getFirebase()
const databaseRef = firebase.database().ref()
const statusRef = databaseRef.child("users").child(userid).child("user_status")
statusRef.set(status).then(() => {
dispatch({ type: 'CHANGE_USER_STATUS' });
});
}
//home.js file
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { firebaseConnect } from 'react-redux-firebase'
import { compose } from 'redux'
import { Redirect } from 'react-router-dom'
import {changeUserStatus} from '../../store/actions/userActions'
class Users extends Component {
handleAcceptClickEvent1 =(e) =>{
console.log("accepted");
const { userid, status } = this.props;
changeUserStatus(e.target.name,"accepted");
}
handleRejectClickEvent1 =(e) =>{
console.log("rejected");
const { userid, status } = this.props;
changeUserStatus(e.target.name,"rejected");
}
reject =(e) =>{
console.log("rejected");
const { userid, status } = this.props;
this.props.changeUserStatus(userid=e.target.name,status="rejected");
}
UserCard = ({user}) =>{
const status = user.user_status;
let acceptComponent;
let rejectComponent;
if (status == "pending"){
acceptComponent = <td><button type="button" class="btn btn-success green" name ={user.user_id} onClick={this.handleAcceptClickEvent1}>Accept</button></td>
rejectComponent = <td><button type="button" class="btn btn-danger red" name ={user.user_id} onClick={this.handleRejectClickEvent1}>Reject</button></td>
}
else if(status == "accepted"){
acceptComponent = <td>Accepted</td>
rejectComponent = <td></td>
}
else{
acceptComponent = <td></td>
rejectComponent = <td>Rejected</td>
}
return (
<tr>
<th scope="row">{user.user_id}</th>
<td>{user.company_name}</td>
<td>{user.email}</td>
{acceptComponent}
{rejectComponent}
</tr>
)
}
render() {
const { users, auth } = this.props;
if (!auth.uid) return <Redirect to='/login' />
console.log(users);
return (
<div className="dashboard container">
<div className="row">
<div className="project-list section">
<div>
<p>Users</p>
</div>
<table className="table table-striped table-bordered" style={{ width: "100%" }}>
<thead>
<tr>
<th scope="col">User Id</th>
<th scope="col">Company Name</th>
<th scope="col">Email</th>
<th scope="col"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{users && users.map(user => {
return <this.UserCard user={user.value} key={user.value.id} />
})}
</tbody>
</table>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
users: state.firebase.ordered.users,
auth: state.firebase.auth
}
}
const mapDispatchToProps = (dispatch) => {
return{
changeUserStatus: (userid,status) => dispatch(changeUserStatus(userid,status))
}
}
export default compose(
connect(mapStateToProps),
firebaseConnect({ users: 'users' })
)(Users);

put this and check
export default compose(
connect(mapStateToProps,mapDispatchToProps),
firebaseConnect({ users: 'users' })
)(Users);

Related

How to pass method to functional component in ReactJS

I'm trying to pass method ref to functional component but somehow it doesn't work
Here is the function:
import { FaTrashAlt } from 'react-icons/fa';
const ArticlesData = props => {
return(
props.products.map(product => {
return (
<tr>
<td>{product.name}</td>
<td>{product.description}</td>
<td>{product.price}$</td>
<td>
<span className="removeProduct--Container" onClick={props.click}>
<FaTrashAlt className="remove--Icon" />
</span>
</td>
</tr>
)
}).reverse()
)
}
export default ArticlesData;
Here is the request I'm trying to pass:
onRemove = (id) => {
fetch(`http://localhost:5000/products/:${id}/delete`, {
method: 'POST'
})
}
And here is how I pass:
<ArticlesData products={this.state.products} click={this.onRemove}/>
Update:
controller:
router.post('/:id/delete', (req, res) => {
try {
console.log(req.params.id)
productService.deleteOne(req.params.id)
res.status(200)
} catch (error) {
console.log(error)
}
})
service:
function deleteOne(id) {
return Product.deleteOne({_id: id});
}
You need to call the function with parameter id.
I'm assuming your product object has id attribute:
import { FaTrashAlt } from 'react-icons/fa';
const ArticlesData = props => {
return(
props.products.map(product => {
return (
<tr>
<td>{product.name}</td>
<td>{product.description}</td>
<td>{product.price}$</td>
<td>
<span className="removeProduct--Container" onClick={() => props.click(product.id)}>
<FaTrashAlt className="remove--Icon" />
</span>
</td>
</tr>
)
}).reverse()
)
}
export default ArticlesData;
Change ArticlesData component's code
from onClick={props.click}
to onClick={() => props.click(product.id)}
Full code:
import React from "react";
import ArticlesData from "./ArticlesData";
export default class SomeName extends React.Component {
onRemove = (id) => {
console.log(id);
fetch(`http://localhost:5000/products/:${id}/delete`, {
method: 'POST'
})
};
render() {
return (
<>
<ArticlesData click={this.onRemove} />
</>
);
}
}
import { FaTrashAlt } from 'react-icons/fa';
const ArticlesData = props => {
return(
props.products.map(product => {
return (
<tr>
<td>{product.name}</td>
<td>{product.description}</td>
<td>{product.price}$</td>
<td>
<span className="removeProduct--Container" onClick={() => props.click(product.id)}>
<FaTrashAlt className="remove--Icon" />
</span>
</td>
</tr>
)
}).reverse()
)
}
export default ArticlesData;
CodeSandbox Demo

Redux-thunk dispatch is not a function error

I tried to call an action from my component. But, When I run the call the below action from my component
I got a type error that tells that dispatch is not a function. how to get rid of this error.
action:
import { FETCH_ALL, FETCH_CUSTOMER, CREATE_CUSTOMER, DELETE_ALL, DELETE_CUSTOMER, UPDATE_CUSTOMER } from '../actionTypes';
import * as api from '../api/index';
export const getCustomers = () => async (dispatch) => {
try {
const { data } = await api.getCustomers();
console.log(dispatch);
dispatch({ type: FETCH_ALL, payload: data});
} catch(err) {
console.log(err);
}
};
component:
function Home() {
const customers = useSelector((state) => state.customers);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getCustomers);
},[dispatch]);
return (
<div style={{paddingLeft: '50px', paddingRight: '50px'}}>
<header>
<h1 style={{textAlign: 'center', color: 'green'}}>Customer Relationship Management</h1>
</header>
<button onClick={dispatch(getCustomers)}>Fetch Customers</button>
<div className="heading">
<h3>Customer Details: </h3>
<button className="homePageButtons"><Link className="homePageLinks" to="/add-customer">Add Customer</Link></button>
</div>
<div className="customerTable">
<table>
<thead>
<tr>
<th>ID</th>
<th className="name">First Name</th>
<th className="name">Last Name</th>
<th className="email">Email</th>
</tr>
</thead>
<tbody>
{customers.map((customer) => (
<tr>
<td>customer.id</td>
<td>customer.firstName</td>
<td>customer.lastName</td>
<td>customer.email</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
The below error occurs:
customer.js:11 TypeError: dispatch is not a function
at customer.js:9
I log the dispatch, it is showing the below object...
SyntheticBaseEvent {_reactName: "onClick", _targetInst: null, type: "click", nativeEvent: MouseEvent, target: button, …}
I applied the middleware in the index.js file ad below...
const store = createStore(reducers, compose(applyMiddleware(thunk)));
In useEffect do dispatch(getCustomers()); in the click handler do onClick={()=>dispatch(getCustomers())}

No access to "this"

I'm working on a web-application using the MERN stack that displays a table of clients with their name, email, and phone number. I haven't implemented Redux quite yet, but I'm using 'uuid' to supplement data in the table until I can get the redux store set up. So far I have displaying the the list and adding a client to the list working fine, but I am having trouble with the pesky delete button.
This is the current ClientTable component
import React, { Component } from "react";
import { Table, Container, Button } from "reactstrap";
import { connect } from "react-redux";
import {
getClients,
addClient,
editClient,
deleteClient,
} from "../actions/clientActions";
import PropTypes from "prop-types";
const renderClient = (clients, index, id) => {
return (
<tr key={index}>
<td>
<Button
className="remove-btn"
color="danger"
size="sm"
onClick={() => {
this.setState((state) => ({
clients: state.clients.filter((client) => client.id !== id),
}));
}}
>
×
</Button>
</td>
<td>{clients.name}</td>
<td>{clients.email}</td>
<td>{clients.number}</td>
</tr>
);
};
class ClientTable extends Component {
componentDidMount() {
this.props.getClients();
}
onDeleteClick = (id) => {
this.props.deleteClient(id);
};
render() {
const { clients } = this.props.client;
// const { clients } = this.state;
return (
<Container id="listContainer">
<Table
id="listTable"
className="table-striped table-bordered table-hover"
dark
>
<tr class="listRow">
<thead id="tableHeader">
<tr>
<th id="listActions">Actions</th>
<th id="listName">Name</th>
<th id="listEmail">Email</th>
<th id="listNumber">Number</th>
</tr>
</thead>
<tbody class="listRow">{clients.map(renderClient)}</tbody>
</tr>
</Table>
</Container>
);
}
}
ClientTable.propTypes = {
getClients: PropTypes.func.isRequired,
client: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
client: state.client,
});
export default connect(mapStateToProps, {
getClients,
deleteClient,
addClient,
})(ClientTable);
This is the bit of code that is causing me issues
<Button
className="remove-btn"
color="danger"
size="sm"
onClick={() => {
this.setState((state) => ({
clients: state.clients.filter((client) => client.id !== id),
}));
}}
>
×
</Button>
When I click the "delete" button I keep getting TypeError: Cannot read property 'setState' of unedefined
I know the error is because of 'this' isn't bound to anything, but I'm uncertain how to bind it within an onClick event if that is even possible or what even to bind it to. I am just lost as to how to approach this problem. (I'm still quite new to React).
If anyone has any ideas it would be greatly appreciated!
move renderClient function to ClientTable, and use it as a method of this class.
class ClientTable extends Component {
componentDidMount() {
this.props.getClients();
}
renderClient = (clients, index) => {
return (
<tr key={index}>
<td>
<Button
className="remove-btn"
color="danger"
size="sm"
onClick={() => this.onDeleteClient(clients.id)}
>
×
</Button>
</td>
<td>{clients.name}</td>
<td>{clients.email}</td>
<td>{clients.number}</td>
</tr>
);
};
onDeleteClick = (id) => {
this.props.deleteClient(id);
};
render() {
const { clients } = this.props.client;
// const { clients } = this.state;
return (
<Container id="listContainer">
<Table
id="listTable"
className="table-striped table-bordered table-hover"
dark
>
<tr class="listRow">
<thead id="tableHeader">
<tr>
<th id="listActions">Actions</th>
<th id="listName">Name</th>
<th id="listEmail">Email</th>
<th id="listNumber">Number</th>
</tr>
</thead>
<tbody class="listRow">{clients.map(this.renderClient)}</tbody>
</tr>
</Table>
</Container>
);
}
}

How to delete an item from a table in 'React' using 'Redux'

Essentially the app at this point displays a list "Students" in the view saved to the data base—now I'd like to be able to delete a Student and have that persist as well. I believe the answer is in the component itself.
Here is what I have thus far—this is my Students component:
import React, { Component } from "react";
import store from "../store";
import { scrubStudent } from "../reducers";
export default class Students extends Component {
constructor(props) {
super(props);
this.state = store.getState();
this.deleteStudent = this.deleteStudent.bind(this);
}
deleteStudent(itemIndex) {
console.log(this.state);
var students = this.state.students;
store.dispatch(scrubStudent(this.state));
students.splice(itemIndex, 1);
this.setState({
students: students
});
}
render() {
var students = this.props.students;
return (
<div className="container">
<div className="sixteen columns">
<h1 className="remove-bottom">Students</h1>
<h5>List of current students and their campus</h5>
<hr />
</div>
<div className="sixteen columns">
<div className="example">
<div>
<table className="u-full-width">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Email</th>
<th>Campus</th>
</tr>
</thead>
<tbody>
{students.map(function(student, index) {
return (
<tr key={index}>
<td>
{student.id}
</td>
<td>
{student.name}
</td>
<td>
{student.email}
</td>
<td>
{student.campus}
</td>
<td>
<a
className="button button-icon"
onClick={this.deleteStudent(index)}
>
<i className="fa fa-remove" />
</a>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</div>
</div>
);
}
}
Right now I get index.js:90 TypeError: Cannot read property 'deleteStudent' of undefined
Thanks in advance!
UPDATE
Based on Matthew's suggestion I sought guidance (I am in school) from a teacher, he helped me wire the following:
But now I am getting the following error:
`index.js:90 TypeError: Cannot read property 'setState' of undefined`
I am going to dig into the reason for that!
import React, { Component } from "react";
import store from "../store";
import { deleteStudent } from "../reducers";
export default class Students extends Component {
constructor(props) {
super(props);
this.state = store.getState();
this.deleteStudent = this.deleteStudent.bind(this);
}
componentDidMount() {
this.unsubscribe = store.subscribe(function() {
this.setState(store.getState());
});
}
componentWillUnmount() {
this.unsubscribe();
}
deleteStudent(index) {
console.log(this.state);
var students = this.state.students;
store.dispatch(deleteStudent(index));
this.state = store.getState();
}
render() {
var students = this.props.students;
return (
<div className="container">
<div className="sixteen columns">
<h1 className="remove-bottom">Students</h1>
<h5>List of current students and their campus</h5>
<hr />
</div>
<div className="sixteen columns">
<div className="example">
<div>
<table className="u-full-width">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Email</th>
<th>Campus</th>
</tr>
</thead>
<tbody>
{students.map(function(student, index) {
return (
<tr key={index}>
<td>
{student.id}
</td>
<td>
{student.name}
</td>
<td>
{student.email}
</td>
<td>
{student.campus}
</td>
<td>
<a
className="button button-icon"
onClick={() => this.deleteStudent(student.id)}
key={index}
>
<i className="fa fa-remove" />
</a>
</td>
</tr>
);
}, this)}
</tbody>
</table>
</div>
</div>
</div>
</div>
);
}
}
This is my reducer:
import { combineReducers } from "redux";
import axios from "axios";
// INITIAL STATE
const initialState = {
students: [],
campuses: []
};
//ACTION CREATORS
const UPDATE_NAME = "UPDATE_NAME";
const ADD_STUDENT = "ADD_STUDENT";
const DELETE_STUDENT = "DELETE_STUDENT";
const GET_STUDENTS = "GET_STUDENTS";
const UPDATE_CAMPUS = "UPDATE_CAMPUS";
const GET_CAMPUS = "GET_CAMPUS";
const GET_CAMPUSES = "GET_CAMPUSES";
// ACTION CREATORS
export function updateName(name) {
const action = {
type: UPDATE_NAME,
name
};
return action;
}
export function addStudent(student) {
return {
type: ADD_STUDENT,
student
};
}
export function scrubStudent(student) {
return {
type: DELETE_STUDENT,
student
};
}
export function getStudents(students) {
const action = {
type: GET_STUDENTS,
students
};
return action;
}
export function updateCampus(campus) {
const action = {
type: UPDATE_CAMPUS,
campus
};
return action;
}
export function getCampus(campus) {
const action = {
type: GET_CAMPUS,
campus
};
return action;
}
export function getCampuses(campuses) {
const action = {
type: GET_CAMPUSES,
campuses
};
return action;
}
//THUNK CREATORS
export function fetchStudents() {
return function thunk(dispatch) {
return axios
.get("/api/students")
.then(function(res) {
return res.data;
})
.then(function(students) {
return dispatch(getStudents(students));
})
.catch(function(err) {
return console.error(err);
});
};
}
export function postStudent(student) {
return function thunk(dispatch) {
return axios
.post("/api/students", student)
.then(function(res) {
return res.data;
})
.then(function(newStudent) {
return dispatch(addStudent(newStudent));
})
.catch(function(err) {
return console.error(err);
});
};
}
export function deleteStudent(student) {
return function thunk(dispatch) {
return axios
.delete("/api/students/" + student.toString())
.then(function(res) {
return res.data;
})
.then(function(student) {
return dispatch(scrubStudent(student));
})
.catch(function(err) {
return console.error(err);
});
};
}
export function fetchCampuses() {
return function thunk(dispatch) {
return axios
.get("/api/campuses")
.then(function(res) {
return res.data;
})
.then(function(campuses) {
return dispatch(getCampuses(campuses));
})
.catch(function(err) {
return console.error(err);
});
};
}
export function postCampus(student) {
return function thunk(dispatch) {
return axios
.post("/api/campuses", campuse)
.then(function(res) {
return res.data;
})
.then(function(newCampus) {
return dispatch(getCampus(newCampus));
})
.catch(function(err) {
return console.error(err);
});
};
}
// REDUCER
const rootReducer = function(state = initialState, action) {
var newState = Object.assign({}, state);
switch (action.type) {
case GET_STUDENTS:
newState.students = state.students.concat(action.students);
return newState;
case ADD_STUDENT:
newState.students = state.students.concat([action.student]);
return newState;
case DELETE_STUDENT:
newState.students = state.students.concat([action.student]);
return newState;
case GET_CAMPUSES:
newState.campuses = state.campuses.concat(action.campuses);
return newState;
case GET_CAMPUS:
newState.campuses = state.campuses.concat([action.campus]);
return newState;
default:
return state;
}
};
export default rootReducer;
You should use arrow function in order to keep the relevant context in the map function:
{students.map((student, index) => {
This way when you use this inside the function - it's your current component.

Redux: using different reducers for a component

I've got a component UsersList which I'd like to reuse with two different reducers - one for listing regular users (state.users.x) and one for listing administrators (state.adminusers.x). The display is the same in both cases, but the state is in different places and different api actions apply (different endpoints with different business rules).
How can I write my component so it can use either reducer?
Write the UsersList component as normal, but do not connect it to redux.
For example:
import React, { Component } from 'react';
import { Table } from 'react-bootstrap';
import UserInviteRow from 'jsx/components/Lib/Users/UserInviteRow';
export class UsersList extends Component {
render() {
const { inviteUserToOrg } = this.props;
return (
<Table bordered hover>
<thead>
<tr>
<th className="width-200">First Name</th>
<th className="width-250">Last Name</th>
<th>Email</th>
<th className="width-150">Last Login</th>
<th className="width-100"> </th>
</tr>
</thead>
<tbody>
<UserInviteRow invitefxn={ inviteUserToOrg }/>
{ this.renderRows() }
</tbody>
</Table>
);
}
renderRows() {
const { usersList } = this.props;
if( ! usersList.length ) {
return (
<tr>
<td colSpan="5">
<em>No users exist for this non-profit</em>
</td>
</tr>
);
}
return usersList.map( (user) => {
return (
<tr key={user.key}>
<td>{user.firstName}</td>
<td>{user.lastName}</td>
<td>{user.correspondenceEmailAddress}</td>
<td>{ (user.lastSeen) ? formatTime(user.lastSeen) : '' }</td>
<td className="text-center">
{ this.renderRemoveButton( user ) }
</td>
</tr>
);
});
}
renderRemoveButton(user) {
const { currentUser } = this.props;
if( currentUser.key === user.key ) {
// users cannot remove themselves
return null;
}
return (
<a className="text-danger" onClick={ () => { this.removeUser(user) } }>
<em className="fa fa-times" />
</a>
);
}
removeUser( user ) {
this.props.removeUserFromOrg(user.key);
}
}
export default UsersList;
Make sure both your reducers implement the action functions you use, in this case inviteUserToOrg and removeUserFromOrg.
Create new container components connected to each reducer
For example:
import { connect } from 'react-redux';
import {
inviteUserToOrg,
removeUserFromOrg
} as actions from 'jsx/redux/modules/nonadminUsers';
import UsersList from 'jsx/components/Lib/Users/UsersList';
var NonadminUserList = connect(
state => {
return {
usersList: state.users.usersList,
};
},
actions
)(UsersList);
export default NonadminUserList;
and
import { connect } from 'react-redux';
import {
inviteUserToOrg,
removeUserFromOrg
} as actions from 'jsx/redux/modules/adminUsers';
import UsersList from 'jsx/components/Lib/Users/UsersList';
var AdminUserList = connect(
state => {
return {
usersList: state.adminusers.usersList,
};
},
actions
)(UsersList);
export default AdminUserList;
Now changes to your presentation component, UsersList, will affect both container components and each container component can reference it's own reducer state and actions.

Resources