Prop keeps returning undefined - javascript

I'm currently trying to create a table where each row has a checkbox that can be enabled or disabled.
I have created 3 files:
ChangeUserGroup.jsx - This is where the data is read from a json file
UserGroupForm.jsx - Starts the form and passes the this.prop.permissions on to the next file, which contains the table and the checkboxes.
TableShowPermissions.jsx - Contains the table and the problematic function + checkbox
I would like for the data in the ChangeUserGroup.jsx state(called groupDetails.permissions) to control if the checkbox for the given permission is initialized with "defaultChecked". But i am getting the following error:
The files contain the following:
ChangeUserGroup.jsx
import React, { Component } from 'react';
import { Helmet } from 'react-helmet';
import { Container, Row, Col, Card, CardHeader, CardBody, Table, Button } from 'reactstrap';
import jsonData from '_testdata/userGroups';
import LoadingIcon from '_components/LoadingIcon';
import UserGroupForm from '_components/UserGroupForm';
class ChangeUserGroup extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
userGroupId: 0,
groupDetails: []
}
}
componentDidMount() {
this.setState({ isLoading: true });
const { userGroupId } = this.props.match.params
this.setState({
userGroupId: userGroupId
});
jsonData.userGroups.filter((a) => a.id.toString() === userGroupId).map((b) => this.setState({
groupDetails: b,
isLoading: false
}));
}
render() {
const { groupDetails, isLoading } = this.state;
if (isLoading) {
return <LoadingIcon />;
}
return (
<>
<Helmet>
<title>{global.siteName + " - Brugergrupper"}</title>
</Helmet>
<main>
<section className="section section-shaped section-lg">
<Container>
<Row className="justify-content-center">
<Col lg="12">
<Card className="bg-secondary shadow border-0">
<CardHeader className="bg-white">
<h1 className="text-center">{groupDetails.name}</h1>
</CardHeader>
<CardBody className="px-lg-5 py-lg-5">
<UserGroupForm
id={groupDetails.id}
groupName={groupDetails.name}
position={groupDetails.position}
permissions={groupDetails.permissions} />
</CardBody>
</Card>
</Col>
</Row>
</Container>
</section>
</main>
</>
);
}
}
export { ChangeUserGroup };
UserGroupForm.jsx
import React, { Component } from 'react';
import { Form } from "reactstrap";
import CustomFormInput from '_components/FormStuff/CustomFormInput';
import TableShowPermissions from '_components/UserGroups/TableShowPermissions';
class UserGroupForm extends Component {
render() {
const { id, groupName, position, permissions } = this.props;
return (
<Form>
<CustomFormInput pLabel="Gruppe navn" pIcon="fa-user" pType="text" pShowLabel="on" pValue={groupName} />
<CustomFormInput pLabel="Gruppe position" pIcon="fa-user" pType="text" pShowLabel="on" pValue={position} />
<hr />
<TableShowPermissions
thePermissions={permissions} />
</Form>
);
}
}
export default UserGroupForm;
TableShowPermissions.jsx
import React, { Component } from 'react';
import jsonData from 'UserPermissions';
import { Table, Button } from "reactstrap";
class TableShowPermissions extends Component {
constructor(props) {
super(props);
this.checkIfPermissionAdded = this.checkIfPermissionAdded.bind(this);
}
checkIfPermissionAdded(checkPerm) {
const { thePermissions } = this.props;
thePermissions.split('|').map(permC => {
//console.log(checkPerm + " -- " + permC)
if (checkPerm === permC) {
return true;
}
});
return false;
}
render() {
return (
<Table className="align-items-center table-bordered" responsive>
<thead className="thead-light">
<tr>
<th scope="col">Permission navn</th>
<th scope="col">Beskrivelse</th>
<th scope="col" />
</tr>
</thead>
<tbody>
{jsonData.permissions.map(perm => (
<tr key={perm.id}>
<th scope="row">
{perm.permission}
</th>
<td>
{perm.description}
</td>
<td className="text-right">
<label className="custom-toggle">
<input type="checkbox" defaultChecked={this.checkIfPermissionAdded(perm.permission)} />
<span className="custom-toggle-slider rounded-circle" />
</label>
</td>
</tr>
))}
</tbody>
</Table>
);
}
}
export default TableShowPermissions;
JSON DATA
{
"userGroups": [
{
"id": 1,
"name": "Administrator",
"position": "Admin",
"permissions": "ADMIN_ROOT|"
},
{
"id": 2,
"name": "Moderator",
"position": "Mod",
"permissions": "ADMIN_SETTINGS_GENERAL|ADMIN_CASES_SEEALL|ADMIN_SETTINGS_CATEGORIES"
},
{
"id": 3,
"name": "Supporters",
"position": "Supporter",
"permissions": "ADMIN_CASES_SEEALL|"
}
]
}
Any help would be appreciated! :)

If after a few react renders you do have the value, it most likely means it just isn't there yet in the first renders (this can happen if you are getting the data from a request).
You can either add an if condition to only run the split when thePermissions exist or add a default value to it.
checkIfPermissionAdded(checkPerm) {
const { thePermissions } = this.props;
if (thePermissions) {
thePermissions.split('|').map(permC => {
//console.log(checkPerm + " -- " + permC)
if (checkPerm === permC) {
return true;
}
});
}
return false;
}
or
checkIfPermissionAdded(checkPerm) {
const { thePermissions = '' } = this.props;
thePermissions.split('|').map(permC => {
//console.log(checkPerm + " -- " + permC)
if (checkPerm === permC) {
return true;
}
});
return false;
}
It is possible that none is what you desire in your code but it might help you out knowing why it is happening.

Initialize your Loading initial state as true. Set your state in the constructor like this:
constructor(props) {
super(props);
this.state = {
isLoading: true,
userGroupId: props.match.params,
groupDetails: []
}
}
Remove the first two setState from your componentDidMount. Each time a setState is called the render fucntion is called.

Related

Unable to render data from API in React, no errors displayed

I am using Django Rest Framework to send data to React app. But the data is being shown on screen.
The code isnt returning any errors thus making it difficult to see whats going wrong. This is my second React project thus i am not too familiar with React & JS as of now.
This is my code:
import { render } from "#testing-library/react";
import axios from "axios";
import React, { Component, useState, useEffect } from "react";
const api_url = "http://localhost:8000/api/CBView/"
class StocksHomePage extends Component {
constructor(props) {
super(props);
this.state = {
isFetching:false,
data_s :[]
};
}
componendDidMount() {
this.fetchData();
this.timer = setInterval(() => this.fetchData(), 50);
}
fetchData = () => {
this.setState({...this.state, isFetching:true});
axios.get(api_url)
.then (response => {
this.setState({data_s:response.data[0]})
})
.catch(e => {
console.log(e);
this.setState({...this.state, isFetching:false});
});
};
render() {
return (
<div>
{this.state.data_s.map(m => <p>{m.co_S}</p>)}
{/* <p data={this.state.data_s.co_S} ></p> */}
<ul>
<li isKey dataField='co_N'></li>
<li dataField='co_S'></li>
<li dataField='price'></li>
</ul>
<p>{this.state.isFetching ? 'Fetching users...' : ''}</p>
</div>
)
}
}
I fixed the issue, all i had to do was include maps function with variable to represent those values. Here is my code:
class StocksHomePage extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
loaded: false,
placeholder: "loading"
};
}
componentDidMount() {
axios
.get("http://localhost:8000/CBView")
.then(response => {return response.data; })
.then(data => {this.setState(() => {
return {data, loaded:true};
});
});
}
handleClick = (props) => {
<HashRouter>
<Route path='/StocksDetail' component={StocksDetail} />
</HashRouter>
};
render() {
return (
<table className="table">
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Price/ Chng</th>
<th>Mkt Cap</th>
<th>Volume</th>
<th>Turnover</th>
</tr>
</thead>
<tbody>
{this.state.data.map(item => {
return (
<tr key={item.co_S}>
<button onCLick={this.handleClick(item.co_S)}><td >{item.co_N}</td></button>
<td>{item.price}</td>
<td>{item.p_chng_pc}</td>
<td>{item.Mkt_cap}</td>
<td>{item.volume}</td>
<td>{item.volume * item.price}</td>
</tr>
);
})};
</tbody>
</table>
);
}
}
export default StocksHomePage;

React setState() needs 2 clicks to update UI

I'm new in React. My question may be common in React developers and there are many same questions but I still don't know how to resolve that. I must still click twice to update UI state. The first click just calls event handler but not update counter variable in state. Even I used the callback form of setState() like the following:
this.setState({ hasButtonBeenClicked: true }, () => {console.log("Clicked")});
the console.log("Clicked") was not reached in first click as well!
App.js
import React, { Component, useState } from "react";
import { Summary } from "./Summary";
import ReactDOM from "react-dom";
let names = ["Bob", "Alice", "Dora"]
function reverseNames() {
names.reverse();
ReactDOM.render(<App />, document.getElementById('root'));
}
function promoteName(name) {
names = [name, ...names.filter(val => val !== name)];
ReactDOM.render(<App />, document.getElementById('root'));
}
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
}
incrementCounter = (increment) => this.setState({counter: this.state.counter + increment});
render() {
return (
<table className="table table-sm table-striped">
<thead>
<tr><th>#</th><th>Name</th><th>Letters</th></tr>
</thead>
<tbody>
{names.map((name, index) =>
<tr key={name}>
<Summary index={index} name={name}
reverseCallback={() => reverseNames()}
promoteCallback={() => promoteName(name)}
counter={this.state.counter}
incrementCallback={this.incrementCounter}
/>
</tr>
)}
</tbody>
</table>
)
}
}
Summary Component
import React, { Component } from "react";
import { SimpleButton } from "./SimpleButton";
export class Summary extends Component {
render() {
const props = this.props;
return (
<React.Fragment>
<td>{props.index + 1} </td>
<td>{props.name} </td>
<td>{props.name.length} </td>
<td>
<SimpleButton
className="btn btn-warning btn-sm m-1"
callback={() => props.reverseCallback()}
text={`Reverse (${props.name})`}
{...this.props}
/>
</td>
</React.Fragment>
)
}
}
SimpleButton
import React, { Component } from "react";
export class SimpleButton extends Component {
constructor(props) {
super(props);
this.state = {
hasButtonBeenClicked: false
}
}
handleClick = (e) => {
this.props.incrementCallback(3);
this.setState({ hasButtonBeenClicked: true });
this.props.callback();
console.log(e);
}
render() {
return (
<button onClick={(e) => this.handleClick(e)}
className={this.props.className}
disabled={this.props.disabled === "true"
|| this.props.disabled === true}>
{ this.props.text} { this.props.counter}
{ this.state.hasButtonBeenClicked &&
<div>Button Clicked!</div>
}
</button>
)
}
}
I resolved the problem by commenting out the line in App.js
function reverseNames() {
names.reverse();
// ReactDOM.render(<App />, document.getElementById('root'));
}
I thinks the line making app rerender before the actual state updated so I was behind the actual state 1 span. The first click is the initial state, the second click is the state after the first click .etc

How can I custom my DataTable in React App

I found a tutorial to implement DataTable in React App but I have no idea how can I custom my tr, td, colspan etc...
My DataTable.js is :
import React, { Component } from 'react';
const $ = require('jquery');
$.DataTable = require('datatables.net');
class App extends Component {
componentDidMount() {
this.$el = $(this.el);
this.$el.DataTable({
data: this.props.data,
columns: this.props.columns
})
}
render() {
return (
<div className="table-responsive">
<table className="table" ref={el => this.el = el}>
</table>
</div>
);
}
}
And after in my Test.js :
import React, { Component } from 'react';
import DataTable from './DataTable';
import axios from 'axios';
class Test extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
columns: [
{ title: "Id" },
{ title: "Category" },
{ title: "Title" },
{ title: "Command 1" },
{ title: "Command 2" },
{ title: "Command 3" }
]
}
}
componentDidMount() {
this._getFilteredItems();
}
_getFilteredItems = () => {
axios.post('http://localhost:8080/items/category', { category: "category1" })
.then((res) => {
var test = res.data.map((e) => Object.values(e)); // to transform objet in array
this.setState({ data: test });
})
.catch(error => { console.log(error) });
}
display = () => {
if(this.state.data.length > 0){
return (
<DataTable
data={this.state.data}
columns={this.state.columns}>
</DataTable>
);
}
}
render() {
return (
<div>
{this.display()}
</div>
);
}
}
export default Test;
My data received from my backend is like this :
[
["5e9c231facad1424801f5167", "category1", "title", "command1", "command2", "command3"],
["5e9c2337acad1424801f58ce", "category1", "title", "command1", "command2", "command3"],
["5eaa105b82d1130017d31dbe", "category1", "title", "command1", "command2", "command3"],
]
The thing is I would like to custom my tr, td, colspan etc... I mean, I would like for example put the title with a colspan="5" and my command1, command2 and command3 in the same td.
Do you have any idea how can I do that ? Thanks
I found, I simply initialized my table as I wanted in the render instead of using props like the tutorial mentionned it :
import React, { Component } from 'react';
import axios from 'axios';
const $ = require('jquery');
$.DataTable = require('datatables.net');
class Test extends Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentDidMount() {
this._getFilteredItems();
}
_getFilteredItems = () => {
axios.post('http://localhost:8080/items/category', { category: "category1" })
.then((res) => {
this.setState({data: res.data});
this.$el = $(this.el);
this.$el.DataTable({});
})
.catch(error => { console.log(error) });
}
render() {
return (
<div className="table-responsive">
<table className="table" ref={el => this.el = el}>
<thead>
<tr>
<th>Title</th>
<th>Commands</th>
</tr>
</thead>
<tbody>
{this.state.data.map((elmt, i) => (
<tr key={i}>
<td>
{elmt.title}
</td>
<td>
{elmt.command1} <br/>
{elmt.command2} <br/>
{elmt.command3} <br/>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}
export default Test;

Update just one value of JSON object and keep the old ones with React Flux

I am new to react/flux and I try to practice, so I try to develop a pizza shop frontend with fake backend (created with FAKER).
My problem is, that I want to update my JSON data, but I can't figure out how and where can I do this. Important, I just want to update "preparedAt" value in my JSON data, I want to keep the other values ("sequentialNumber", "id", "name").
How can I keep the old values, and just update only one value?
For example, I want to update this:
{"sequentialNumber": 1,"id": 61301,"name": "molestiae","preparedAt": "2020-05-02T21:16:16.687Z"},
to this:
{"sequentialNumber": 1,"id": 61301,"name": "molestiae", "preparedAt": "2020-04-01"}
I have a search action in my code too, but it's working well.
Here are my JSON file and code:
jsonFile.json
"preppizzas": [
{
"sequentialNumber": 1,
"id": 61301,
"name": "molestiae",
"preparedAt": "2020-05-02T21:16:16.687Z"
},
{
"sequentialNumber": 2,
"id": 21349,
"name": "impedit",
"preparedAt": "2020-05-02T23:25:48.105Z"
},
{
"sequentialNumber": 3,
"id": 10235,
"name": "beatae",
"preparedAt": "2020-05-02T21:33:28.759Z"
},
{
"sequentialNumber": 4,
"id": 99688,
"name": "dicta",
"preparedAt": "2020-05-02T19:24:48.462Z"
}
]
PizzaDispatcher.js
import {Dispatcher} from 'flux';
import axios from 'axios';
import Preparationstore from "./stores/PreparationStore";
class PizzaDispatcher extends Dispatcher{
handleViewAction(action){
this.dispatch({
action : action
});
}
}
const dispatcher = new PizzaDispatcher();
dispatcher.register((payload)=>{
if(payload.action.actionType === 'PREPARATION_SEARCH') {
if (payload.action.payload.id === "") {
axios.get('/preppizzas').then((resp)=>{
Preparationstore._preparations = resp.data.filter((preparation)=>{
return preparation.preparedAt.includes(payload.action.payload.preparedAt)
});
Preparationstore.emitChange();
})
}
else {
axios.get('/preppizzas').then((resp) => {
Preparationstore._preparations = resp.data.filter((preparation) => {
return preparation.id == payload.action.payload.id;
});
Preparationstore.emitChange();
})
}
}
if(payload.action.actionType === 'PREPARATION_UPDATE'){
if(payload.action.payload.id !==''){
axios.put('/preppizzas/' + payload.action.payload.id,{
preparedAt : payload.action.payload.preparedAt,
}).then(resp=>{console.log(resp.data)}).catch(err => {console.log(err)
});
}
}
});
export default dispatcher;
PreparationActions.js
import dispatcher from '../PizzaDispatcher'
class PreparationActions{
search(id, preparedAt){
dispatcher.handleViewAction({
actionType : 'PREPARATION_SEARCH',
payload : {
id : id,
preparedAt : preparedAt
}
});
}
update(preparedAt){
dispatcher.handleViewAction({
actionType : 'PREPARATION_UPDATE',
payload : {
preparedAt : preparedAt
}
});
}
}
export default new PreparationActions;
PreparationStore.js
import EventEmitter from 'events'
class PreparationStore extends EventEmitter{
_preparations = [];
emitChange(){
this.emit('Change');
}
addChangeListener(callback){
this.on('Change', callback);
}
removeChangeListener(callback){
this.removeListener('Change', callback);
}
}
var Preparationstore = new PreparationStore();
export default Preparationstore;
import React from 'react';
import PreparationActions from "../../../../actions/PreparationActions";
PreparationsCRUD.js
class PreparationsCRUD extends React.Component{
constructor(){
super();
this.state={
id : "",
preparedAt : ""
}
}
render(){
return(
<div>
<table>
<tr>
<td><input
type={"number"} min="0" placeholder="ID"
value={this.state.id}
onChange={(e)=>{
let st = this.state;
st.id = e.target.value;
this.setState(st);}
}
onKeyDown={(e) => {
if (e.key === 'Enter') {
PreparationActions.search(this.state.id, this.state.preparedAt);
}
}}
/></td>
</tr>
<tr>
<td><input type={"text"} placeholder="Prepared at"
value={this.state.preparedAt}
onChange={(e)=>{
let st = this.state;
st.preparedAt = e.target.value;
this.setState(st);
}}
/></td>
</tr>
<tr>
<td
colSpan={2}>
<button
className="btn btn-info"
onClick={()=>{PreparationActions.search(this.state.id, this.state.preparedAt)
}}
>Search by ID
</button>
<button
className="btn btn-info"
onClick={()=>{
PreparationActions.update(
this.state.preparedAt,
);window.location.reload();}}
>Update
</button>
</td>
</tr>
</table>
</div>
);
}
}
export default PreparationsCRUD;
PreparationsResults.js
import React from 'react';
import Preparationstore from "../../../../stores/PreparationStore";
class PreparationsResult extends React.Component{
constructor(){
super();
this.state = {preparations : []};
this._onChange = this._onChange.bind(this);
}
_onChange(){
this.setState({preparations : Preparationstore._preparations})
}
componentDidMount(){
Preparationstore.addChangeListener(this._onChange)
}
componentWillUnmount(){
Preparationstore.removeChangeListener(this._onChange);
}
render(){
return(
<table className="table table-dark">
<thead>
<tr>
<td>Sequential number</td>
<td>Id</td>
<td>Name</td>
<td>Prepared at</td>
</tr>
</thead>
<tbody>
{
this.state.preparations.map((preparation)=>{
return(
<tr key={preparation.sequentialNumber}>
<td>{preparation.sequentialNumber}</td>
<td>{preparation.id}</td>
<td>{preparation.name}</td>
<td>{preparation.preparedAt}</td>
</tr>
);
})
}
</tbody>
</table>
);
}
}
export default PreparationsResult;
I literally just learned this earlier today!
You can use the spread operator in a reducer to keep all of the object the same and modify only one parameter. Hope this helps.
const personReducer = (person, action) => {
switch (action.type) {
case 'INCREASE_AGE':
return { ...person, age: person.age + 1 };
case 'CHANGE_LASTNAME':
return { ...person, lastname: action.payload.lastname };
default:
return person;
}
};

How to select all checkboxes in React?

I have this module:
import React, { Component } from 'react'
import EmailListItem from './EmailListItem'
import { createContainer } from 'meteor/react-meteor-data'
import { Emails } from '../../../../../imports/collections/emails/Emails'
class EmailList extends Component {
constructor (props) {
super(props)
this.state = {
selectedEmails: new Set(),
checked: false
}
}
handleSelectedEmails (selectedEmail, checked) {
let selectedEmails = this.state.selectedEmails
if (checked) {
selectedEmails.add(selectedEmail)
} else {
selectedEmails.delete(selectedEmail)
}
this.setState({selectedEmails})
console.log('selectedEmails', this.state.selectedEmails)
}
removeSelected () {
const selectedEmails = Array.from(this.state.selectedEmails)
Meteor.call('emails.remove', selectedEmails, (err, result) => {
if (err) console.log(err)
if (result) console.log(result)
})
}
checkedClick () {
this.setState({checked: !this.state.checked})
console.log('chcekedClick')
}
renderList () {
console.log(this.props)
return this.props.emails.map(email => {
console.log(email)
const { name, opr, ctr, _id } = email
const createdAt = email.createdAt.toDateString()
const link = `/dashboard/emailpreview/${_id}`
return (
<EmailListItem
selecetedAllEmails={this.state.checked}
handleSelectedEmails={this.handleSelectedEmails.bind(this)}
name={name}
createdAt={createdAt}
opr={opr}
ctr={ctr}
link={link}
key={email._id}
id={email._id} />
)
})
}
render () {
// TODO: make checks with state
return (
<div className="email_list">
<table>
<thead>
<tr>
<td><input onChange={this.checkedClick.bind(this)} type="checkbox" checked={this.state.checked} /></td>
<td>Title<button onClick={this.removeSelected.bind(this)} className="btn btn-danger">Remove</button></td>
<td>Dates</td>
<td>Open Rates</td>
<td>CTA</td>
</tr>
</thead>
<tbody>
{this.renderList()}
</tbody>
</table>
</div>
)
}
}
export default createContainer(() => {
Meteor.subscribe('emails')
return { emails: Emails.find({}).fetch() }
}, EmailList)
And it renders this module
import React, { Component } from 'react'
import { Link } from 'react-router'
class EmailListItem extends Component {
constructor (props) {
super(props)
this.state = {
checked: false
}
}
checkedClick () {
this.setState({checked: !this.state.checked})
console.log('chcekedClick')
}
componentDidUpdate () {
console.log('componentDidUpdate')
const { myCheckbox } = this.refs
console.log('myCheckbox', myCheckbox)
console.log('myCheckbox.name', myCheckbox.name)
console.log('myCheckbox.checked', myCheckbox.checked)
if (this.props.selecetedAllEmails) {
console.log('componentDidUpdate IF')
this.checkedClick()
this.props.handleSelectedEmails(myCheckbox.name, myCheckbox.checked)
}
}
render () {
console.log('_id', this.props.id)
return (
<tr>
<td><input ref="myCheckbox"
onChange={(event) => {
this.checkedClick()
this.props.handleSelectedEmails(event.target.name, event.target.checked)
}}
checked={this.state.checked}
type="checkbox" name={this.props.id} /></td>
<td><Link to={this.props.link}>{this.props.name}</Link></td>
<td>{this.props.createdAt}</td>
<td>Open Rates</td>
<td>CTA</td>
</tr>
)
}
}
export default EmailListItem
As you can see, for each email item I have a checkbox. I can select a few checkboxes, and click that remove button which will call remove my selected items. Now in the top I have a checkbox which should select all the checkboxes. My solution to this was to store the global checkbox checked and pass it as a prop to all the items. Then in the items I perform a check on componentDidUpdate and if the global checkbox is selected then I check that item as well. But this results in an infinite loop. What would be the best solution here?
While some of the answers provided the specific functionality of selecting all the checkboxes, I needed also common functionalities like deselecting all, selecting all then deselecting some, when manually selecting all the select all box checks on as well etc... So I wrote all of that and post it as an answer here. Thanks to everyone who replied. This code is based on Mayank Shuklas answer. Note that it may not still be perfect as I didn't properly tested it yet and definitely it needs some refactoring.
import React, { Component } from 'react'
import EmailListItem from './EmailListItem'
import { createContainer } from 'meteor/react-meteor-data'
import { Emails } from '../../../../../imports/collections/emails/Emails'
class EmailList extends Component {
constructor (props) {
super(props)
this.state = {
selectedEmails: new Set(),
checked: false
}
}
handleSelectedEmails (allSelected, individualSelected, selectedEmail, checked) {
console.log('allSelected', allSelected)
console.log('individualSelected', individualSelected)
console.log('selectedEmail', selectedEmail)
console.log('checked', checked)
let selectedEmails = this.state.selectedEmails
if (allSelected && !individualSelected) {
this.props.emails.forEach((email) => {
selectedEmails.add(email._id)
})
} else if (!allSelected && !individualSelected) {
selectedEmails.clear()
} else if (individualSelected) {
if (checked) {
selectedEmails.add(selectedEmail)
if (selectedEmails.size === this.props.emails.length) {
this.checkAll()
}
} else {
selectedEmails.delete(selectedEmail)
this.setState({checked})
}
}
this.setState({selectedEmails})
console.log('selectedEmails', this.state.selectedEmails)
}
removeSelected () {
const selectedEmails = Array.from(this.state.selectedEmails)
Meteor.call('emails.remove', selectedEmails, (err, result) => {
if (err) console.log(err)
if (result) console.log(result)
})
}
checkAll () {
this.setState({checked: !this.state.checked})
console.log('chcekedClick', this.state.checked)
this.handleSelectedEmails(!this.state.checked, false)
}
renderList () {
console.log(this.props)
return this.props.emails.map(email => {
// console.log(email)
const { name, opr, ctr, _id } = email
const createdAt = email.createdAt.toDateString()
const link = `/dashboard/emailpreview/${_id}`
return (
<EmailListItem
handleSelectedEmails={this.handleSelectedEmails.bind(this)}
name={name}
createdAt={createdAt}
opr={opr}
ctr={ctr}
link={link}
key={email._id}
id={email._id}
value={this.state.checked || this.state.selectedEmails.has(email._id)} />
)
})
}
render () {
// TODO: make checks with state
return (
<div className="email_list">
<table>
<thead>
<tr>
<td><input onChange={this.checkAll.bind(this)} type="checkbox" checked={this.state.checked} /></td>
<td>Title<button onClick={this.removeSelected.bind(this)} className="btn btn-danger">Remove</button></td>
<td>Dates</td>
<td>Open Rates</td>
<td>CTA</td>
</tr>
</thead>
<tbody>
{this.renderList()}
</tbody>
</table>
</div>
)
}
}
export default createContainer(() => {
Meteor.subscribe('emails')
return { emails: Emails.find({}).fetch() }
}, EmailList)
And the EmailListItem
import React, { Component } from 'react'
import { Link } from 'react-router'
class EmailListItem extends Component {
render () {
console.log('_id', this.props.id)
return (
<tr>
<td><input ref="myCheckbox"
onChange={(event) => {
this.props.handleSelectedEmails(false, true, event.target.name, event.target.checked)
}}
checked={this.props.value}
type="checkbox" name={this.props.id} /></td>
<td><Link to={this.props.link}>{this.props.name}</Link></td>
<td>{this.props.createdAt}</td>
<td>Open Rates</td>
<td>CTA</td>
</tr>
)
}
}
export default EmailListItem
I think, maintaining individual states for each email id is not required, you are already storing the values in parent component, pass the value from parent in props, other thing is for selecting all email ids, you are maintaining a bool in parent, at the time of passing the value in props check that bool, if the bool is true then pass true otherwise check in the set and pass the result returned by set.
Check the working solution of jsfiddle: https://jsfiddle.net/h17mcjwa/
try this renderList method:
renderList () {
return this.props.emails.map(email => {
const { name, opr, ctr, _id } = email
const createdAt = email.createdAt.toDateString()
const link = `/dashboard/emailpreview/${_id}`
return (
<EmailListItem
handleSelectedEmails={this.handleSelectedEmails.bind(this)}
name={name}
createdAt={createdAt}
opr={opr}
ctr={ctr}
link={link}
key={email._id}
id={email._id}
value={this.state.checked || this.state.selectedEmails.has(email._id)}
/>
)
})
}
And use this component:
class EmailListItem extends Component {
constructor (props) {
super(props)
//this.state = {
// checked: false
//}
}
//checkedClick () {
// this.setState({checked: !this.state.checked})
// console.log('chcekedClick')
//}
//componentDidUpdate () {
// if (this.props.selecetedAllEmails) {
// this.checkedClick()
// this.props.handleSelectedEmails(myCheckbox.name, myCheckbox.checked)
// }
//}
render () {
return (
<tr>
<td><input ref="myCheckbox"
onChange={(event) => {
this.props.handleSelectedEmails(event.target.name, event.target.checked)
}}
checked={this.props.value}
type="checkbox" name={this.props.id} /></td>
<td><Link to={this.props.link}>{this.props.name}</Link></td>
<td>{this.props.createdAt}</td>
<td>Open Rates</td>
<td>CTA</td>
</tr>
)
}
}
Let me know if it doesn't work for u.
You could use componentWillReceiveProps instead of componentDidUpdate:
class EmailListsItem extends Component {
// ...
componentWillReceiveProps (nextProps) {
const { myCheckbox } = this.refs
// if selecetedAllEmails is updated from false to true
if (nextProps.selecetedAllEmails && !this.props.selecetedAllEmails) {
this.checkedClick()
this.props.handleSelectedEmails(myCheckbox.name, myCheckbox.checked)
}
}
// ...
}

Categories

Resources