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
Related
Can anyone help me explain it, because when use functional it work, but not when use class component
Different class component and func component in handle event react.
fyi it take input text search on child and at parent doing handle event and other
child use functional
import React from "react";
const Header = ({searchTitle,onSearch}) => {
return (
<div>
<header>
<h1>Notes</h1>
<div className="search">
<input
type="text"
placeholder="Search..."
value={searchTitle}
onChange={onSearch}
></input>
</div>
</header>
</div>
);
};
export default Header;
child use class component
import React, { Component } from "react";
export class HeaderPage extends Component {
render() {
const searchTitle = this.props.searchTitle;
const onSearch = this.props.onSearchHandler;
return (
<header>
<h1>Notes</h1>
<div className="search">
<input
type="text"
placeholder="Search..."
value={searchTitle}
onChange={onSearch}
></input>
</div>
</header>
);
}
}
export default HeaderPage;
parent class componet
import React, { Component } from "react";
import HeaderPage from "../organisms/HeaderPage";
import MainPage from "../organisms/MainPage";
import FooterPage from "../organisms/FooterPage";
import { getInitialData } from "../../utils/data";
import Header from "../organisms/Header";
export class PageNote extends Component {
constructor(props) {
super(props);
this.state = {
dataNotes: getInitialData(),
dataNotesFiltered: [],
searchTitle: "",
};
//binding
this.onAddNoteHandler = this.onAddNoteHandler.bind(this);
this.onDeleteHandler = this.onDeleteHandler.bind(this);
this.onArchivedHandler = this.onArchivedHandler.bind(this);
this.onSearchHandler = this.onSearchHandler.bind(this);
}
onAddNoteHandler({ title, body }) {
this.setState((prevState) => {
return {
dataNotes: [
...prevState.dataNotes,
{
id: +new Date(),
title,
body,
archived: false,
createdAt: new Date().toLocaleDateString(),
},
],
};
});
}
onDeleteHandler(id) {
const dataNotes = this.state.dataNotes.filter(
(dataNote) => dataNote.id !== id
);
this.setState({ dataNotes });
}
onArchivedHandler(id) {
const dataNotes = this.state.dataNotes.map((note) => {
if (note.id === id) {
return { ...note, archived: !note.archived };
} else {
return note;
}
});
this.setState({ dataNotes });
}
onSearchHandler(event) {
this.setState(() => {
return {
searchTitle: event.target.value,
};
});
}
render() {
console.log(this.state.dataNotes);
console.log(`search ${this.state.searchTitle}`);
const dataNotes = this.state.dataNotes.filter((note) =>
note.title.toLowerCase().includes(this.state.searchTitle.toLowerCase())
);
return (
<>
<HeaderPage
searchTitle={this.state.searchTitle}
onSearch={this.onSearchHandler}
></HeaderPage>
<Header
searchTitle={this.state.searchTitle}
onSearch={this.onSearchHandler}
></Header>
<MainPage
dataNotes={dataNotes}
addNote={this.onAddNoteHandler}
onDelete={this.onDeleteHandler}
onArchive={this.onArchivedHandler}
></MainPage>
<FooterPage></FooterPage>
</>
);
}
}
export default PageNote;
i have tried to remove todo task from my state array but its not working for me. i am having hard time using deletHandle function in Todolist.js. how can i use key from state to indentify and delete clicked(or perticular) todo task
App.js
import React from 'react';
import { Form } from './components/todo-Form/Form.component';
import { Todolist } from './components/Todolist/Todolist.component';
import './App.css';
class App extends React.Component {
constructor() {
super();
this.state = {
todo: [],
do: ""
}
}
render() {
const handleChange = e => {
this.setState({ do: e.target.value }/*, () => { console.log(e) }*/);
};
const submitHandler = e => {
e.preventDefault();
if (this.state.do != "" ) {
this.setState({ todo: [...this.state.todo, { task: this.state.do, id: Math.random() * 1000 }], do: "" }/*, () => { console.log(this.state.todo) }*/)
}
}
const deletHandler = key => {
this.setState({
todo: this.state.todo.filter(el => el !== key)
})
}
return (
<div className="App">
<header>
<h1>ToDo List</h1>
</header>
<Form handleChange={handleChange} submitHandler={submitHandler} inputText={this.state.do} />
<Todolist check={this.state.todo} deletHandler={deletHandler}/>
</div>
)
}
}
export default App;
Todolist.js
import React from "react";
import { Todo } from "../Todo/Todo.component";
import "./todolist.style.css"
export const Todolist = ({check, deletHandler}) => {
return <div >
<ul className="todo-list">
{check.map(todo => (<Todo task={todo.task} key={todo.id} onClick={() => {deletHandler(todo.id)}}/>))}
</ul>
</div>;
};
Todo.js
import React from "react";
import "./todo.style.css";
export const Todo = ({task}) =>{
return (
<div className="todo-container" >
<li className="todo">
<h2> {task} </h2>
<ul className="delet-x">X</ul>
</li>
</div>)
}
In your delete handler, you have to use the id to delete
const deletHandler = (key) => {
this.setState({
todo: this.state.todo.filter((el) => el.id !== key),
});
};
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.
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>
I'm trying to create a simple todo list app where you can type a name and a task into two inputs and create a list of tasks after hitting the add task button. I've been debugging this for hours and I can't figure out why taskAdded isn't getting detected! I have other code from a udemy course very similar to this that works fine, I'm not sure why this isn't working as well. Here's a couple snippets of code from my project. Anything would help! :)
AddTask.js
import React, { Component } from "react";
class AddTask extends Component {
state = {
name: "",
task: ""
};
nameChanged = event => {
this.setState({ name: event.target.value });
};
taskChanged = event => {
this.setState({ task: event.target.value });
};
render() {
return (
<div className="AddTask">
<input
type="text"
placeholder="Name"
onChange={this.nameChanged}
value={this.state.name}
/>
<input
type="text"
placeholder="Task"
onChange={this.taskChanged}
value={this.state.task}
/>
<button onClick={() => this.props.taskAdded(this.state.name, this.state.task)}>Add Task</button>
</div>
);
}
}
export default AddTask;
Tasks.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Task from '../components/Task/Task';
import AddTask from '../components/AddTask/AddTask';
import * as actionTypes from '../store/actions';
class Tasks extends Component {
render () {
return (
<div>
<AddTask taskAdded={this.props.onAddedtask} />
{this.props.tsk.map(task => (
<Task
key={task.id}
name={task.name}
task={task.task}
clicked={() => this.props.onRemovedTask(task.id)}/>
))}
</div>
);
}
}
const mapStateToProps = state => {
return {
tsk: state.tasks
};
};
const mapDispatchToProps = dispatch => {
return {
onAddedTask: (name, task) => dispatch({type: actionTypes.ADD_TASK, taskData: {name:name, task: task}}),
onRemovedTask: (id) => dispatch({type: actionTypes.REMOVE_TASK, taskId: id})
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Tasks);
You have a typo. onAddedTask needs a capital T in
<AddTask taskAdded={this.props.onAddedtask}