For some reason, the onClick will not fire once, but if clicked twice. The this.props.PostLike(id) action will be fired, What should i do to make it work only on one click.
Also, the heart state works fine, in just one click. Its just the this.props.postLike needs to be clicked twice for it work.
And using e.preventDefault() does not solve the issue.
....
clickLike = (id) => {
this.props.postLike(id);
// toggles between css class
this.setState({
heart: !this.state.heart
})
}
render(){
return(
<div style={{float:'right', fontSize: '1.5em', color:'tomato'}} >
<i style={{ marginRight: '140px'}} className={this.state.heart ? 'fa fa-heart':'fa fa-heart-o' }>
<span style={{ marginLeft: '6px'}}>
<a href="#" onClick={() =>this.clickLike(this.props.like)}>Like</a>
</span>
{/* gets the like counts */}
<span style={{ marginLeft: '7px'}} >{this.props.likes} </span>
</i>
</div>
)
}
}
const mapStateToProps = (state) => ({
})
const mapDispatchToProps = (dispatch) => ({
postLike: (id) => dispatch( postLike(id))
// Pass id to the DeletePost functions.
});
export default connect(null, mapDispatchToProps)(Like);
PostItem.js
import React, { Component } from 'react';
import Paper from '#material-ui/core/Paper';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import moment from 'moment';
import Editable from './Editable';
import {connect} from 'react-redux';
import {UpdatePost, postLike, getCount} from '../actions/';
import Like from './Like';
import Axios from '../Axios';
const Styles = {
myPaper: {
margin: '20px 0px',
padding: '20px'
},
button:{
marginRight:'30px'
}
}
class PostItem extends Component{
constructor(props){
super(props);
this.state = {
disabled: false,
myId: 0,
likes:0
}
}
onUpdate = (id, title) => () => {
// we need the id so expres knows what post to update, and the title being that only editing the title.
if(this.props.myTitle !== null){
const creds = {
id, title
}
this.props.UpdatePost(creds);
}
}
render(){
const {title, id, userId, removePost, createdAt, post_content, username, editForm, isEditing, editChange, myTitle, postUpdate, Likes, clickLike, myLikes} = this.props
return(
......
{/* likes get like counts */}
<Like like={id} likes={myLikes} />
.......
Posts.js
render() {
const {loading} = this.state;
const {myPosts} = this.props
console.log(this.state.posts);
if (!this.props.isAuthenticated) {
return (<Redirect to='/signIn'/>);
}
if (loading) {
return "loading..."
}
return (
<div className="App" style={Styles.wrapper}>
<h1>Posts</h1>
{/* <PostList posts={this.state.posts}/> */}
<div>
{this.state.posts.map(post => (
<Paper key={post.id} style={Styles.myPaper}>
<PostItem myLikes={post.Likes.length} // right here
myTitle={this.state.title} editChange={this.onChange} editForm={this.formEditing} isEditing={this.props.isEditingId === post.id} removePost={this.removePost} {...post}/>
</Paper>
))}
</div>
</div>
);
}
}
PostList.js
import React, { Component } from 'react';
import Paper from '#material-ui/core/Paper';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import moment from 'moment';
import {connect} from 'react-redux';
import { withRouter, Redirect} from 'react-router-dom';
import {DeletePost, postLike, UpdatePost,EditChange, GetPosts, getCount, DisableButton} from '../actions/';
import PostItem from './PostItem';
import _ from 'lodash';
const Styles = {
myPaper: {
margin: '20px 0px',
padding: '20px'
}
}
class PostList extends Component{
constructor(props){
super(props);
this.state ={
title: '',
posts:[],
loading:true
}
}
componentWillMount() {
this.props.GetPosts();
}
componentWillReceiveProps(nextProps, prevState) {
let hasNewLike = true;
if (prevState.posts && prevState.posts.length) {
for (let index = 0; index < nextProps.myPosts.length; index++) {
if (nextProps.myPosts[index].Likes.length !== prevState.posts[index].Likes.length) {
hasNewLike = true;
}
}
}
if (hasNewLike) {
this.setState({posts: nextProps.myPosts, loading: false}); // here we are updating the posts state if redux state has updated value of likes
}
}
// Return a new function. Otherwise the DeletePost action will be dispatch each
// time the Component rerenders.
removePost = (id) => () => {
this.props.DeletePost(id);
}
onChange = (e) => {
e.preventDefault();
this.setState({
title: e.target.value
})
}
formEditing = (id) => ()=> {;
this.props.EditChange(id);
}
render(){
const { posts, loading} = this.state;
// console.log(this.props.posts)
// console.log(this.props.ourLikes);
if(loading){
return "loading..."
}
return (
<div>
{this.state.posts.map(post => (
<Paper key={post.id} style={Styles.myPaper}>
<PostItem
myLikes={post.Likes.length} // right here
myTitle={this.state.title}
editChange={this.onChange}
editForm={this.formEditing}
isEditing={this.props.isEditingId === post.id}
removePost={this.removePost}
{...post}
/>
</Paper>
))}
</div>
);
}
}
const mapStateToProps = (state) => ({
isEditingId: state.post.isEditingId,
myPosts: state.post.posts,
})
const mapDispatchToProps = (dispatch) => ({
// pass creds which can be called anything, but i just call it credentials but it should be called something more
// specific.
GetPosts: () => dispatch(GetPosts()),
EditChange: (id) => dispatch(EditChange(id)),
UpdatePost: (creds) => dispatch(UpdatePost(creds)),
postLike: (id) => dispatch( postLike(id)),
// Pass id to the DeletePost functions.
DeletePost: (id) => dispatch(DeletePost(id))
});
// without withRouter componentWillReceiveProps will not work like its supposed too.
export default withRouter(connect(mapStateToProps,mapDispatchToProps)(PostList));
Try doing like that:
clickLike = (id) => () => {
this.props.postLike(id);
// toggles between css class
this.setState({
heart: !this.state.heart
})
}
<a href="#" onClick={this.clickLike(this.props.like)}>Like</a>
This likely has little to do with the logic you have set up and more about the html markup itself. If you were to take in the event and console.log it, you'll probably find that you are actually not hitting the tag on the first click, but one of its outer elements. Try wrapping the tag and everything else inside of it in a tag then putting the onClick logic in the button instead and you'll yield better results.
<div style={{float:'right', fontSize: '1.5em', color:'tomato'}} >
<button onClick={() =>this.clickLike(this.props.like)}>
<i style={{ marginRight: '140px'}} className={this.state.heart ? 'fa fa-heart':'fa fa-heart-o' }>
<span style={{ marginLeft: '6px'}}>
Like
</span>
{/* gets the like counts */}
<span style={{ marginLeft: '7px'}} >{this.props.likes} </span>
</i>
</button>
div>
You have to be careful when using arrow functions with the this keyword as it does not refer to the current object, but its parent object instead.
clickLike = async(id) => {
await this.props.postLike(id);
// toggles between css class
this.setState({
heart: !this.state.heart
})
}
try using the async await
<span style={{ marginLeft: '6px'}} onClick=
{()=>this.clickLike(this.props.like)}>
<a href="#" >Like</a>
</span>
Related
I have written a simple piece of code to open another tab using React. It currently does not work. I think that something very simple has gone wrong but I am unsure where.
Message.component.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { fetchData, GET } from '../DAL';
import Inbox from "/Messages/GetMessages";
<Route path="/Messages/GetMessages" component={MENU.inbox} />
handleClick()
{
//this will open the inbox in a new tab
window.open("/Messages/GetMessages");
}
I have added the reference in the Dal.js file
Dal.js
export function fetchData(API)
{
return getAsync(API);
}
async function getAsync(API)
{
try
{
let response = await fetch(API);
let data = await response.json();
data = JSON.parse(data);
return data;
}
catch (e)
{
console.log("ERROR FOUND IN " + API, e);
return [];
}
}
export const GET = {
INBOX:'Messages/GetMessages'}
It is also in the menu component, so that it can be seen where it is used.
Menu.component.jsx
import React from 'react';
import { Button, Icon } from 'semantic-ui-react'
export const MENU = {
INBOX: 'INBOX'
};
export default class MainMenu extends React.Component {
constructor(props)
{
super(props)
this.state = {
active: MENU.ASSETS
};
}
_onClick = (item) =>
{
if (this.state.active !== item)
{
this.setState({active: item})
}
() => this.props.visible();
}
_triggerUserProfile()
{
showEditUserForm(51, 10)
}
render() {
let buttonStyle = {backgroundColor: '#5d7090', color: 'white'};
let icon = this.props.icon;
return (
<MainMenuContainer>
<div style={{margin: '5px'}}>
<img width='75px' style={{marginBottom: '5px'}} src="Content/img/logo_white.svg"/>
<IconButton onClick={() => this.props.visible()}>
<Icon name={icon}></Icon>
</IconButton>
<span onClick={() => this._triggerUserProfile()} style={{float: 'right', color: 'white', marginBottom: 'auto', marginTop:'7px', cursor: 'pointer'}}>Welcome Back, Alex! <Icon name='chevron down'></Icon></span>
</div>
<div>
<Button animated='vertical' style={buttonStyle} onClick={() =>
this.props.handleClick(MENU.INBOX)}>
<Button.Content hidden>Inbox</Button.Content>
<Button.Content visible>
<Icon name='mail' />
</Button.Content>
</Button>
</div>
</MainMenuContainer>
Currently nothing happens and the react crashes silently.
You can try like this : window.open("url","_self");
You're looking for
window.open('url', '_blank');
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
I have this component that I split for easy management. Before splitting everything worked as expected, after splitting I am getting an error when I click on an icon which calls the createReactionsIcon. Error says
TypeError: updateReaction is not a function
onClick
./components/home/components/SingleUpload.jsx:26
23 |
24 | return icons.map(({ key, text, type }) => (
25 | <IconText
> 26 | onClick={() => updateReaction(item.id, key)}
| ^ 27 | key={key}
28 | type={type}
29 | text={text}
How can I access this correctly from my Home component where updateReaction is returning updateReaction from the redux store.
SubComponent
import PropTypes from 'prop-types';
import React from 'react';
import { Avatar, Card, Icon, List } from 'antd';
import { LIST_TEXTS, STYLES } from '../constants';
const { AVATAR, CARD_CONTAINER, CARD_LIST, ICON, USER_LIST } = STYLES;
const { INNER, MORE, UPLOAD, VERTICAL } = LIST_TEXTS;
const IconText = ({ type, text, onClick }) => (
<span>
<Icon type={type} style={ICON} onClick={onClick} />
{text}
</span>
);
function createReactionsIcon(item, updateReaction) {
const { like, dislike, maybe } = item.reactions;
const icons = [
{ key: 'like', text: `${like.count}`, type: 'heart' },
{ key: 'dislike', text: `${dislike.count}`, type: 'dislike' },
{ key: 'maybe', text: `${maybe.count}`, type: 'meh' },
];
return icons.map(({ key, text, type }) => (
<IconText
onClick={() => updateReaction(item.id, key)}
key={key}
type={type}
text={text}
/>
));
}
export default class SingleUpload extends React.Component {
render() {
const { values } = this.props;
return (
<div style={CARD_CONTAINER}>
<List
itemLayout={VERTICAL}
dataSource={values}
renderItem={item => {
const { avatar, description, id, uploader: { image, name } } = item;
return (
<List.Item style={USER_LIST}>
<Card
actions={createReactionsIcon(item, this.updateReaction)}
cover={<img alt={UPLOAD} src={image} />}
extra={<Icon type={MORE} />}
hoverable
key={id}
title={(
<a href="/">
<Avatar src={avatar} style={AVATAR} />
{name}
</a>
)}
type={INNER}
style={CARD_LIST}
>
{description}
</Card>
</List.Item>
);
}}
/>
</div>
);
}
}
Home.js
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import SingleUpload from './SingleUpload';
import ComparisonUpload from './ComparisonUpload';
import { STYLES } from '../constants';
import * as actions from '../actions';
import { getUploads } from '../selectors';
const { CARD_CONTAINER } = STYLES;
class Home extends React.Component {
componentDidMount() {
const { actions: { requestUploadList } } = this.props;
requestUploadList();
}
updateReaction = (id, reaction) => {
const { actions: { updateReaction } } = this.props;
const payload = { id, reaction };
updateReaction(payload);
}
render() {
const { uploads } = this.props;
return (
<div style={CARD_CONTAINER}>
<SingleUpload values={[...uploads.values()]} />
<ComparisonUpload values={[...uploads.values()]} />
</div>
);
}
}
Home.propTypes = {
actions: PropTypes.objectOf(PropTypes.object),
uploads: PropTypes.instanceOf(Map),
};
const mapStateToProps = state => ({
uploads: getUploads(state),
});
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(actions, dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Pass your function to component as props,
<SingleUpload values={[...uploads.values()]} updateReaction = {this.updateReaction}/>
Now you can use this in your child component,
<IconText onClick={() => this.props.updateReaction(item.id, key)}
You can pass the updateReaction from your parent to child as a callback
<SingleUpload values={[...uploads.values()]} hanldeReaction={this.updateReaction} />
And you can access it in the child using props.hanldeReaction
<Card actions={createReactionsIcon(item, this.props.hanldeReaction)}
You have to pass down the updateReaction() event-handler you defined in Home as a prop to SingleUpload. Then you can access that prop from anywhere inside your component.
Which means we can cleanup the actions prop inside the Card since we only need to pass the item now.
<Card actions={createReactionsIcon(item)}
As well as createReactionsIcon, now we just call that prop directly inside the function
function createReactionsIcon(item) {
const { like, dislike, maybe } = item.reactions;
const icons = [
{ key: 'like', text: `${like.count}`, type: 'heart' },
{ key: 'dislike', text: `${dislike.count}`, type: 'dislike' },
{ key: 'maybe', text: `${maybe.count}`, type: 'meh' },
];
return icons.map(({ key, text, type }) => (
<IconText
onClick={() => this.props.updateReaction(item.id, key)}
key={key}
type={type}
text={text}
/>
));
}
Less redundant code overall which sounds like what you are trying to achieve.
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
I am near the end of creating my application.
So it is for banks accounts where they ask you to give the first letter of your password, then for example fourth, etc.
I'm tired of counting on my own so I created this app.
But there is the last bug that I don't know how to fix.
So when I press "1" I get "1 - H", and then when I press "4" I want to get:
"1 - H" (clicked before)
"4 - X" (clicked just now)
but instead, I get:
"4 - X" (clicked just now)
"4 - X" (clicked just now)
So it is caused by the way handleResults() function works inside my Input component, but for now it is my only concept how to approach this...
import React, { Component } from 'react';
import TextField from 'material-ui/TextField';
import './style.css';
import Buttons from '../Buttons';
import Results from '../Results';
class Input extends Component {
constructor(props) {
super(props);
this.state = {
password: 'Hh9Xzke2ayzcEUPHuIfS',
selectedButtons: [],
};
this.handleButtonSelectTwo = this.handleButtonSelectTwo.bind(this);
}
handleInputChange(pass) {
this.setState({ password: pass });
}
handleButtonSelectTwo(selected) {
this.setState({
selectedButtons: [...this.state.selectedButtons, selected],
});
}
handleResults() {
return this.state.selectedButtons.map(el => (
<Results key={el} appState={this.state} />
));
}
render() {
return (
<div>
<div className="Input-textfield">
<TextField
hintText="Paste your password here to begin"
value={this.state.password}
onChange={event => this.handleInputChange(event.target.value)}
/>
</div>
<div>
<Buttons
handleButtonSelectOne={this.handleButtonSelectTwo}
array={this.state.password.length}
/>
{this.handleResults()}
</div>
</div>
);
}
}
export default Input;
and here is Results component code:
import React, { Component } from 'react';
import _ from 'lodash';
import Avatar from 'material-ui/Avatar';
import List from 'material-ui/List/List';
import ListItem from 'material-ui/List/ListItem';
import './style.css';
const style = {
avatarList: {
position: 'relative',
left: -40,
},
avatarSecond: {
position: 'relative',
top: -40,
left: 40,
},
};
class Results extends Component {
resultsEngine(arg) {
const { selectedButtons, password } = this.props.appState;
const passwordArray = password.split('').map(el => el);
const lastSelectedButton = _.last(selectedButtons);
const passwordString = passwordArray[_.last(selectedButtons) - 1];
if (arg === 0) {
return lastSelectedButton;
}
if (arg === 1) {
return passwordString;
}
return null;
}
render() {
if (this.props.appState.selectedButtons.length > 0) {
return (
<div className="test">
<List style={style.avatarList}>
<ListItem
disabled
leftAvatar={<Avatar>{this.resultsEngine(0)}</Avatar>}
/>
<ListItem
style={style.avatarSecond}
disabled
leftAvatar={<Avatar>{this.resultsEngine(1)}</Avatar>}
/>
</List>
</div>
);
}
return <div />;
}
}
export default Results;
Anyone has an idea how should I change my code inside handleResults() function to achieve my goal? Any help with solving that problem will be much appreciated.
Buttons component code:
import React from 'react';
import OneButton from '../OneButton';
const Buttons = props => {
const arrayFromInput = props.array;
const buttonsArray = [];
for (let i = 1; i <= arrayFromInput; i++) {
buttonsArray.push(i);
}
const handleButtonSelectZero = props.handleButtonSelectOne;
const allButtons = buttonsArray.map(el => (
<OneButton key={el} el={el} onClick={handleButtonSelectZero} />
));
if (arrayFromInput > 0) {
return <div>{allButtons}</div>;
}
return <div />;
};
export default Buttons;
And OneButton code:
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
const style = {
button: {
margin: 2,
padding: 0,
minWidth: 1,
},
};
class OneButton extends Component {
constructor() {
super();
this.state = { disabled: false };
}
handleClick() {
this.setState({ disabled: !this.state.disabled });
this.props.onClick(this.props.el);
}
render() {
return (
<RaisedButton
disabled={this.state.disabled}
key={this.props.el}
label={this.props.el}
style={style.button}
onClick={() => this.handleClick()}
/>
);
}
}
export default OneButton;
In your resultsEngine function in the Results component you are specifying that you always want the _.last(selectedButtons) to be used. This is what it is doing, hence you always see the last button clicked. What you actually want is the index of that iteration to show.
const lastSelectedButton = selectedButtons[this.props.index];
const passwordString = passwordArray[selectedButtons[this.props.index]];
To get an index you have to create and pass one in, so create it when you map over the selected Buttons in the handleResults function in your Input component.
handleResults() {
return this.state.selectedButtons.map((el, index) => (
<Results key={el} appState={this.state} index={index} />
));
}