How to avoid rerendering element in the array in react? - javascript

I have a array and I want to render the this array into a few redux forms. I found out that all the forms are rerendered. the code looks like the following:
Form.jsx
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link, Events, scrollSpy } from 'react-scroll';
import styles from './Form.css';
import MultipleForm from './MultipleForm';
class Form extends Component {
constructor(props) {
super(props);
const {
workflows,
} = this.props;
this.state = {
curTab: workflows.length > 0 ? workflows[0] : '',
curForm: '',
};
}
componentDidMount() {
Events.scrollEvent.register('begin');
Events.scrollEvent.register('end');
scrollSpy.update();
}
componentWillReceiveProps(nextProps) {
const {
workflows,
} = nextProps;
if (workflows && workflows.length > this.props.workflows) {
this.setState({
curTab: workflows[0],
});
}
}
componentWillUnmount() {
Events.scrollEvent.remove('begin');
Events.scrollEvent.remove('end');
}
handleChangeTab = (value) => {
this.setState({
curTab: value,
});
}
handleActiveTab = (workflow) => {
console.log(workflow);
}
render() {
const {
workflows,
schemaNames,
...rest
} = this.props;
return (
<div className={styles.container}>
<header>
<PerspectiveBar
value={this.state.curTab}
onChange={this.handleChangeTab}
style={{
position: 'fixed',
left: '0',
top: '48px',
width: '100vw',
zIndex: '1380',
}}
>
{workflows.map(wf => (
<PerspectiveTab
key={wf}
label={wf}
value={wf}
onActive={() => this.handleActiveTab(wf)}
/>
))}
</PerspectiveBar>
</header>
<div className={styles.formContainer}>
<Paper className={styles.paperContainer}>
<MultipleForm
workflow={this.state.curTab}
schemaNames={schemaNames}
{...rest}
/>
</Paper>
</div>
<Drawer className={styles.drawer} containerStyle={{ height: 'calc(100% - 104px)', top: '104px' }}>
<div className={styles.drawerContainer}>
{schemaNames.map(schemaName => (
<Link
onSetActive={(to) => {
this.setState({
curForm: to,
});
}}
to={schemaName}
duration={500}
offset={-104}
spy
smooth
>
<MenuItem
checked={this.state.curForm === schemaName}
>
{schemaName}
</MenuItem>
</Link>
))}
</div>
</Drawer>
</div>
);
}
}
Form.propTypes = {
schemaNames: PropTypes.arrayOf(PropTypes.string),
workflows: PropTypes.arrayOf(PropTypes.string),
fetchSchemaNames: PropTypes.func.isRequired,
};
Form.defaultProps = {
schemaNames: [],
workflows: [],
};
export default Form;
MultipleForm.jsx
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import FlatButton from 'material-ui/FlatButton';
import { Element } from 'react-scroll';
import SchemaForm from './SchemaForm';
class MultipleForm extends Component {
componentDidMount() {
console.log('MultipleForm Mounted');
const {
workflow,
fetchSchemaNames,
} = this.props;
if (workflow) fetchSchemaNames(workflow);
}
componentWillReceiveProps(nextProps) {
const {
workflow,
fetchSchemaNames,
} = nextProps;
if (workflow && this.props.workflow !== workflow) fetchSchemaNames(workflow);
}
componentDidUpdate() {
const {
schemaNames,
schemas,
initialValues,
fetchSchemas,
fetchInitialValues,
} = this.props;
const schemasNeedToFetch = this.remainingSchemas(schemaNames, schemas);
if (schemasNeedToFetch.length !== 0) fetchSchemas(schemasNeedToFetch);
const initialValuesNeedToFetch = this.remainingInitialValues(schemaNames, initialValues);
if (initialValuesNeedToFetch.lenght !== 0) fetchInitialValues(initialValuesNeedToFetch, 1);
}
remainingSchemas = (schemaNames, schemas) =>
schemaNames.filter(schemaName => schemaName in schemas === false).sort();
remainingInitialValues = (schemaNames, initialValues) =>
schemaNames.filter(schemaName => schemaName in initialValues === false).sort();
handleSubmitAll = (event) => {
event.preventDefault();
const {
submit,
schemas,
schemaNames,
} = this.props;
schemaNames
.map(schemaName => schemas[schemaName].title)
.forEach((title) => {
submit(title);
});
}
render() {
const {
schemaNames,
schemas,
initialValues,
postForm,
} = this.props;
schemaNames.sort((a, b) => a.localeCompare(b));
return (
<div>
{schemaNames.map(schemaName => (
<Element name={schemaName}>
<SchemaForm
key={schemaName}
schema={schemas[schemaName]}
initialValue={initialValues[schemaName]}
schemaName={schemaName}
postForm={postForm}
/>
</Element>
))}
<div>
<FlatButton
label="Submit"
/>
<FlatButton label="Deploy" />
</div>
</div>);
}
}
MultipleForm.propTypes = {
workflow: PropTypes.string.isRequired,
submit: PropTypes.func.isRequired,
fetchSchemaNames: PropTypes.func.isRequired,
schemas: PropTypes.object,
schemaNames: PropTypes.arrayOf(PropTypes.string),
initialValues: PropTypes.object,
fetchSchemas: PropTypes.func.isRequired,
fetchInitialValues: PropTypes.func.isRequired,
postForm: PropTypes.func.isRequired,
};
MultipleForm.defaultProps = {
schemaNames: [],
};
export default MultipleForm;
SchemaForm
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Liform from 'liform-react';
import theme from './NokiaTheme';
import styles from './Form.css';
class SchemaForm extends Component {
componentDidMount() {
console.log('schema mounted');
}
shouldComponentUpdate() {
return false;
}
handleSubmit = (value) => {
const {
postForm,
schemaName,
} = this.props;
postForm(value, schemaName, 1);
}
render() {
const {
schema,
initialValue,
} = this.props;
console.log('props', this.props);
return (
<div>
<h3 id={schema.$id} className={styles.formTitle}>
{schema.title}
</h3>
<Liform
schema={schema}
onSubmit={value => this.handleSubmit(value)}
destroyOnUnmount={false}
theme={theme}
initialValues={initialValue}
/>
</div>
);
}
}
SchemaForm.propTypes = {
schema: PropTypes.shape({
$id: PropTypes.string,
}),
initialValue: PropTypes.object,
schemaName: PropTypes.string,
postForm: PropTypes.func.isRequired,
};
SchemaForm.defaultProps = {
schema: {},
initialValue: null,
schemaName: '',
};
export default SchemaForm;
the schemaNames will be changed only by adding or deleting some element. for example: the schemaNames will change from ['A', 'B', 'C'] to ['A', 'B', 'D']. I get the schemaNames from the redux. which I fetch online.
But when I check the ConnectedReduxForm, when I change the schemaNames, the SchemaForm will be unmounted and the react will mount the form again. I have tried with setting the ConnectedReduxForm to be PureComponent. It is not helpful.
Could someone help me with that? I have spent a lot of time of this and nothing helps.
Update: I have found the problem, the reason of it is that for each time that I for each time I update the workflow, I need to fetch the schemaNames from the server. But I still do not know why this happended. Could someone explain that?

Related

How to pass my onSucceeded() function to the parent component?

I have 2 components OptinPage (parent) and TermsOfServices (child). Optin Page is only used for rendering the TermsOfServices component, which can be reused elsewhere in the application. I want to use my onSucceeded () function from my child component to my parent component. I don't see how to do it at all. Currently the result is such that when I click on the button that validates the TermsOfServices it seems to be an infinite loop, it goes on and on without closing my popup. Before I split my TermsOfServices component into a reusable component it worked fine. Before, all content was gathered in OptinPage. Any ideas? Thanks in advance
my TermsOfServices component:
import API from 'api';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import {
Block,
BlockTitle,
Col,
Fab,
Icon,
Preloader,
} from 'framework7-react';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-refetch';
import ReactHtmlParser from 'react-html-parser';
class TermsOfServices extends PureComponent {
static propTypes = {
agreeTosFunc: PropTypes.func.isRequired,
agreeTos: PropTypes.object,
onSucceeded: PropTypes.func,
tos: PropTypes.object.isRequired,
};
static contextTypes = {
apiURL: PropTypes.string,
loginToken: PropTypes.string,
userId: PropTypes.string,
};
static defaultProps = {
agreeTos: {},
onSucceeded: () => {},
};
state = {
currentTos: -1,
};
componentDidUpdate(prevProps) {
const {
agreeTos,
onSucceeded,
tos,
} = this.props;
const { currentTos } = this.state;
/* Prepare for first tos after receiving all of them */
if (
prevProps.tos.pending &&
tos.fulfilled &&
tos.value.length &&
currentTos < 0
) {
this.setState({ currentTos: 0 });
}
/* When sending ToS agreement is done */
if (
prevProps.agreeTos.pending &&
agreeTos.fulfilled
) {
onSucceeded();
}
}
handleNext = () => {
const { agreeTosFunc, tos } = this.props;
const { currentTos: currentTosId } = this.state;
const termsOfServices = tos.value;
const done = currentTosId + 1 === termsOfServices.length;
this.setState({ currentTos: currentTosId + 1 });
if (done) {
agreeTosFunc(termsOfServices.map((v) => v._id));
}
};
render() {
const { tos } = this.props;
const { currentTos: currentTosId } = this.state;
const termsOfServices = tos.value;
const currentTermsOfServices = termsOfServices && termsOfServices[currentTosId];
const loaded = termsOfServices && !tos.pending && tos.fulfilled;
const htmlTransformCallback = (node) => {
if (node.type === 'tag' && node.name === 'a') {
// eslint-disable-next-line no-param-reassign
node.attribs.class = 'external';
}
return undefined;
};
return (
<div>
{ (!loaded || !currentTermsOfServices) && (
<div id="
optin_page_content" className="text-align-center">
<Block className="row align-items-stretch text-align-center">
<Col><Preloader size={50} /></Col>
</Block>
</div>
)}
{ loaded && currentTermsOfServices && (
<div id="optin_page_content" className="text-align-center">
<h1>
<FormattedMessage id="press_yui_tos_subtitle" values={{ from: currentTosId + 1, to: termsOfServices.length }} />
</h1>
<BlockTitle>
{ReactHtmlParser(
currentTermsOfServices.title,
{ transform: htmlTransformCallback },
)}
</BlockTitle>
<Block strong inset>
<div className="tos_content">
{ReactHtmlParser(
currentTermsOfServices.html,
{ transform: htmlTransformCallback },
)}
</div>
</Block>
<Fab position="right-bottom" slot="fixed" color="pink" onClick={() => this.handleNext()}>
{currentTosId + 1 === termsOfServices.length &&
<Icon ios="f7:check" aurora="f7:check" md="material:check" />}
{currentTosId !== termsOfServices.length &&
<Icon ios="f7:chevron_right" aurora="f7:chevron_right" md="material:chevron_right" />}
</Fab>
{currentTosId > 0 && (
<Fab position="left-bottom" slot="fixed" color="pink" onClick={() => this.setState({ currentTos: currentTosId - 1 })}>
<Icon ios="f7:chevron_left" aurora="f7:chevron_left" md="material:chevron_left" />
</Fab>
)}
</div>
)}
</div>
);
}
}
export default connect.defaults(new API())((props, context) => {
const { apiURL, userId } = context;
return {
tos: {
url: new URL(`${apiURL}/tos?outdated=false&required=true`),
},
agreeTosFunc: (tos) => ({
agreeTos: {
body: JSON.stringify({ optIn: tos }),
context,
force: true,
method: 'PUT',
url: new URL(`${apiURL}/users/${userId}/optin`),
},
}),
};
})(TermsOfServices);
My OptIn Page :
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import {
Link,
NavRight,
Navbar,
Page,
Popup,
} from 'framework7-react';
import { FormattedMessage, intlShape } from 'react-intl';
import './OptInPage.scss';
import TermsOfServices from '../components/TermsOfServices';
class OptinPage extends PureComponent {
static propTypes = {
logout: PropTypes.func.isRequired,
opened: PropTypes.bool.isRequired,
};
static contextTypes = {
intl: intlShape,
logout: PropTypes.func,
};
render() {
const { opened, logout } = this.props;
const { intl } = this.context;
const { formatMessage } = intl;
return (
<Popup opened={opened} className="demo-popup-swipe" tabletFullscreen>
<Page id="optin_page">
<Navbar title={formatMessage({ id: 'press_yui_tos_title' })}>
<NavRight>
<Link onClick={() => logout()}>
<FormattedMessage id="press_yui_comments_popup_edit_close" />
</Link>
</NavRight>
</Navbar>
</Page>
<TermsOfServices onSucceeded={this.onSuceeded} />
</Popup>
);
}
}
export default OptinPage;
Just add the data you want the parent to be supplied with in the child component (when it is hit) and then handle the data passed to the parent in the function that you pass in onSuccess.
This will roughly look like this:
const {useState, useEffect} = React;
function App(){
return <Child onSuccess={(data)=>{console.log(data)}}/>;
}
function Child({onSuccess}){
return <div>
<button
onClick={()=>onSuccess("this is the data from the child component")}>
Click to pass data to parent
</button>
</div>;
}
ReactDOM.render(<App/>,document.getElementById('app'));
#element {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='app'></div>
<div id="element">
<div>node 1</div>
<div>node 2</div>
</div>
to access to parent method or attribute you should use super,
for call to the parent constructor
super([arguments]);
for call parent method
super.parentMethod(arguments);
I recommend create a method on child class and then call the parent method, not directly
for more information take a look on this
https://www.w3schools.com/jsref/jsref_class_super.asp

How to Update the state in react redux-saga

I am newbie to react, redux-saga, I have a dropdown in page Display, when I select it move to respective component (eg. policy, score), In Component Pages, I have a button Add New, on clicking it will navigate to a page as mentioned in link url , which is a page having cancel button, on cancelling it returns to the Display.js but no dropdown selected,
I would like to keep the state articleMode, when navigating to a page and returning back to same page,
articleMode returns to state -1 instead of selected component Policy or Score
actions.js
export const updateArticleMode = data => {
console.log(data.body);
return {
type: CONSTANTS.UPDATE_ARTICLE_MODE,
data: data.body
};
};
queryReducer.js
import * as CONSTANTS from "../constants/constants";
const initialState = {
articleMode: ""
}
case CONSTANTS.UPDATE_ARTICLE_MODE: {
return {
...state,
articleMode: data.mode
};
}
export default queryReducer;
constants.js
export const UPDATE_ARTICLE_MODE = "UPDATE_ARTICLE_MODE";
Display.js
import React from "react";
import { connect } from "react-redux";
import Policy from "../policy";
import Score from "./../score";
import { updateArticleMode } from "../../../../actions/actions";
const articleMode = [
{ name: "Select", id: "-1" },
{ name: "Score", id: "Score" },
{ name: "Policy", id: "Policy" }
]
class Display extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
articleMode: "-1"
};
}
componentWillMount = () => {
this.setState({ articleMode: this.props.queryData.articleMode || "-1" });
};
_getComponent = () => {
const { articleMode } = this.state;
if (articleMode === "Policy") {
return <DisplayPolicy></DisplayPolicy>;
}
if (articleMode === "Score") {
return <DisplayScore></DisplayScore>;
}
}
render() {
return (
<React.Fragment>
<select name="example"
className="simpleSearchSelect1"
value={this.state.articleMode}
onChange={event => {
this.setState({ articleMode: event.target.value });
this.props.dispatch(
updateArticleMode({ body: { mode: event.target.value } })
);
}}
style={{ marginLeft: "2px" }}
>
{articleMode.length != 0 &&
articleMode.map((option, index) => {
const { name, id } = option;
return (
<option key={index} value={id}>
{name}
</option>
);
})}
</select>
{this.state.articleMode === "-1"
? this._renderNoData()
: this._getComponent()}
</React.Fragment>
)}
const mapStateToProps = state => {
return {
queryData: state.queryData
};
};
export default connect(mapStateToProps)(Display);
}
DisplayPolicy.js
import React from "react";
class DisplayPrivacyPolicy extends React.Component {
constructor(props) {
super(props);
}<Link
to={{
pathname: "/ui/privacy-policy/addNew",
state: {
language: "en"
}
}}
>
<button className="page-header-btn icon_btn display-inline">
<img
title="edit"
className="tableImage"
src={`${process.env.PUBLIC_URL}/assets/icons/ic_addstore.svg`}
/>
{`Add New`}
</button>
</Link>
AddNew.js
<Link
to =pathname: "/ui/display",
className="btn btn-themes btn-rounded btn-sec link-sec-btn"
>
Cancel
</Link>

How to combine Material-UI's snackbar and input components in react?

I'm using Material-UI components to build my website. I have a header component with a search field which uses mui InputBase under the hood. When user enters empty input (that is they don't enter anything and just click enter) I want to display a mui Snackbar which will warn the user the no meaningful input was entered.
I can't get the combination of the two components to work together. In addition because search field state doesn't really change when user enters nothing it doesn't reload so if user repeatedly presses Enter the snackbar won't appear. I use this.forceUpdate(); but is there a more elegant way to implement such logic?
This is my code:
for the search input field:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import InputBase from '#material-ui/core/InputBase';
import { withStyles } from '#material-ui/core/styles';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { getAppInfo } from '../../actions/appActions.js';
import constants from '../../constants.js';
import { AppSearchBarInputStyles } from '../styles/Material-UI/muiStyles.js';
import AppNotFound from './AppNotFound.js';
class AppSearchBarInput extends Component {
state = {
appId: ''
}
onChange = e => {
this.setState({ appId: e.target.value });
}
onKeyDown = e => {
const { appId } = this.state;
if (e.keyCode === constants.ENTER_KEY && appId !== '') {
this.props.getAppInfo({ appId });
this.setState({
appId: ''
});
}
this.props.history.push('/app/appInfo');
this.forceUpdate();
}
render() {
const { classes } = this.props;
const { appId } = this.state;
console.log(`appId from AppSearchInput=${appId === ''}`);
return (
<div>
<InputBase
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
value={this.state.appId} />
{ appId === '' ? <AppNotFound message={constants.MESSAGES.APP_BLANK()}/> : ''}
</div>
)
}
}
AppSearchBarInput.propTypes = {
classes: PropTypes.object.isRequired
}
const AppSearchBarWithStyles = withStyles(AppSearchBarInputStyles)(AppSearchBarInput);
const AppSearchBarWithStylesWithRouter = withRouter(AppSearchBarWithStyles);
export default connect(null, { getAppInfo })(AppSearchBarWithStylesWithRouter);
for the snackbar:
import React from 'react';
import Snackbar from '#material-ui/core/Snackbar';
import constants from '../../constants.js';
import SnackbarMessage from './SnackbarMessage.js';
class AppNotFound extends React.Component {
state = {
open: true,
};
handleClose = event => {
this.setState({ open: false });
};
render() {
const { message } = this.props;
return (
<Snackbar
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
}}
open={this.state.open}
autoHideDuration={6000}
onClose={this.handleClose}
>
<SnackbarMessage
onClose={this.handleClose}
variant="warning"
message={message}
/>
</Snackbar>
);
}
}
export default AppNotFound;
I think the good way to achieve what You want is by adding another state property called snackBarOpen which will help You to determine if user has entered empty value or something meaningful:
AppSearchBarInput Component
state = {
appId: '',
snackBarOpen: false
}
handleKeyDown = (e) => {
if (e.keyCode === 13 && e.target.value === '') {
this.setState({
appId: e.target.value,
snackBarOpen: true
});
} else {
this.setState({
appId: e.target.value
})
}
}
handleCloseSnackBar = () => {
this.setState({
snackBarOpen: false
});
}
And then just render also <AppNotFound /> in render() method(it will be hidden by default because it will depend on open prop):
render() {
const { snackBarOpen } = this.state;
return(
<div>
/* <InputBase /> here */
<AppNotFound message={/* Your message here */} open={snackBarOpen} onClose={this.handleCloseSnackBar} />
</div>
)
}
AppNotFound Component
You can remove all methods now and leave only render() one which will look next:
render() {
const { message, open, onClose } = this.props;
return (
<Snackbar
// ...
open={open}
// ...
onClose={onClose}
>
<SnackbarMessage
onClose={onClose}
// ...
message={message}
/>
</Snackbar>
);
}
Hope that my answer will be useful :)

Cannot read property 'push' of undefined React

I am trying to push/redirect to /home after deleting poll. But i get Cannot read property 'push' of undefined
import React, { Component } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { vote, deletePoll } from "../store/actions/polls";
import { Pie } from "react-chartjs-2";
class Poll extends Component {
constructor(props) {
super(props);
this.handlePollDelete = this.handlePollDelete.bind(this);
}
handlePollDelete() {
const { deletePoll, id, history } = this.props;
deletePoll(id);
history.push("/home");
}
render() {
const { poll, vote, auth } = this.props;
console.log(auth.user.id);
const userIdPoll = poll.user && poll.user._id;
const userIdAuth = auth.user.id;
const answers =
poll.question &&
poll.options.map(option => (
<button
className="vote-btn"
onClick={() => vote(poll._id, { answer: option.title })}
key={option._id}
>
{option.title}
</button>
));
const data = poll.options && {
labels: poll.options.map(option => option.title),
datasets: [
{
label: poll.question,
backgroundColor: poll.options.map(option => color()),
borderColor: "#323643",
data: poll.options.map(option => option.votes)
}
]
};
return (
<div className="flex-basic-column" style={{ margin: "35px 0 0 0" }}>
<h3>{poll.question}</h3>
{answers}
{userIdAuth === userIdPoll ? (
<button
className="vote-btn delete-btn"
onClick={this.handlePollDelete}
>
delete
</button>
) : null}
{poll.options && <Pie data={data} />}
</div>
);
}
}
export default connect(
state => ({
poll: state.currentPoll,
auth: state.auth
}),
{ vote, deletePoll }
)(Poll);
You want to use the withRouter HOC on your connected component so that the history prop is given to your component.
import { withRouter } from 'react-router-dom';
export default withRouter(connect(
state => ({
poll: state.currentPoll,
auth: state.auth
}),
{ vote, deletePoll }
)(Poll));

How to access main class function from material UI button component?

I want to call function handleKeyPress() from ToDo.js file in AddButton.js file to set action onClick() for a button from Material UI. May be I should set onClick action for button in some different way ? But any types of import/export declarations dos not work for me.
I want to understand some functions access rules, I'm newbie in JS )
Please tell me what is wrong here.
AddButton.js
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import AddIcon from '#material-ui/icons/Add';
import {handleKey} from '../ToDo';
const styles = theme => ({
button: {
margin: theme.spacing.unit,
},
});
function FloatingActionButtons(props) {
const { classes } = props;
return (
<div>
<Button variant="fab" color="primary" aria-label="Add" className={classes.button} onClick={() => (handleKey)}>
<AddIcon />
</Button>
</div>
);
}
FloatingActionButtons.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(FloatingActionButtons);
ToDo.js
import React, {Component} from 'react';
import './ToDo.css';
import ToDoItem from './components/ToDoItem';
import AppBar from './components/AppBar';
import AddButton from './components/AddButton';
import Logo from './assets/logo.png';
const appBar = <AppBar />
const addButton = <AddButton />
class ToDo extends Component {
constructor(props) {
super(props);
this.state = {
list: [
{
title: 'Cup cleaning',
todo: "Wash and take away the Kurzhiy's cup from WC"
},
{
title: 'Smoking rollton',
todo: 'Do some rollton and cigarettes'
},
{
title: 'Curious dream',
todo: 'Build a time machine'
}
],
title: '',
todo: ''
};
};
createNewToDoItem = () => {
this.setState(({ list, title, todo }) => ({
list: [
...list,
{
title,
todo
}
],
title: '',
todo: ''
}));
};
handleKeyPress = e => {
if (e.target.value !== '') {
if (e.key === 'Enter') {
this.createNewToDoItem();
}
}
};
handleTitleInput = e => {
this.setState({
title: e.target.value,
});
};
handleTodoInput = e => {
this.setState({
todo: e.target.value
});
};
deleteItem = indexToDelete => {
this.setState(({ list }) => ({
list: list.filter((toDo, index) => index !== indexToDelete)
}));
};
editItem = (i, updTitle, updToDo) => {
let arr = this.state.list;
arr[i].title = updTitle;
arr[i].todo = updToDo;
this.setState ({list: arr});
};
eachToDo = (item, i) => {
return <ToDoItem
key={i}
title={item.title}
todo={item.todo}
deleteItem={this.deleteItem.bind(this, i)}
editItem={this.editItem.bind(this, i)}
/>
};
render() {
return (
<div className="ToDo">
<img className="Logo" src={Logo} alt="React logo"/>
<h1 className="ToDo-Header">{appBar}</h1>
<div className="ToDo-Container">
<div className="ToDo-Content">
{this.state.list.map(this.eachToDo)}
</div>
<div>
<input type="text" placeholder="Enter new title" value={this.state.title} onChange={this.handleTitleInput} onKeyPress={this.handleKeyPress}/>
<input type="text" placeholder="Enter new todo" value={this.state.todo} onChange={this.handleTodoInput} onKeyPress={this.handleKeyPress}/>
<button className="ToDo-Add" onClick={this.createNewToDoItem}>+</button>
<p>{addButton}</p>
</div>
</div>
</div>
);
}
}
export default ToDo;
export const handleKey = this.handleKeyPress;
console.log(handleKey)
You refer with this to a property or a method not to a variable, so you shouldn't be using this here at all.
Secondly you will have to import handleKeyPress method to your file first to be able to access it, are you sure that the method is in const { classes } = props; here ? and if yes you should then point the onclick={handleKeyPress()} just to one function it is not necessary to create an arrow function which returns a function { classes } and not to the props, then it should be working

Categories