Passing react function to children down, event.target empty - reactjs

I'm struggling to grasp a react concept that to me is likely used all the time.
I have an app with a state.
I have a section below app.
Below section I have clickable tile that receives a function to update app status. This works, however the event.target appears to be null.
I'm passing the function to update the status all the way down from app as a prop.
How can I fix this / what am I missing?
import React, { Component } from 'react';
import './App.css';
const Section = ({ handleClick }) => {
return (
<div className="section">
Section
<Tile handleClick={handleClick} title="1" />
<Tile handleClick={handleClick} title="2" />
<Tile handleClick={handleClick} title="3" />
</div>
)
}
const Tile = ({ handleClick, title }) => {
return (
<div className="tile" onClick={handleClick}>
tile {title}
</div>
)
};
class App extends Component {
constructor(props) {
super(props);
this.state = {
modalOpen: false
};
}
openModal = () => {
this.setState({
modalOpen: true,
openedBy: ""
})
}
closeModal = (event) => {
this.setState({
modalOpen: false,
openedBy: event.target.title
})
}
render() {
return (
<div className="App">
<div>ModalOpen = {this.state.modalOpen.toString()}</div>
<div>Opened by = {this.state.openedBy}</div>
<Section handleClick={this.openModal}></Section>
<a href="#" onClick={this.closeModal}>Close modal</a>
</div>
);
}
}
export default App;
Thanks so much for pointer in the right direction!

You are not passing down a title prop to your Tile component, but your are passing down a number prop.
You can create a new function in the Tile component that calls the handleClick with the number, which you then use to set the openedBy in your App.
Example
const Section = ({ handleClick }) => {
return (
<div className="section">
Section
<Tile handleClick={handleClick} number="1" />
<Tile handleClick={handleClick} number="2" />
<Tile handleClick={handleClick} number="3" />
</div>
);
};
const Tile = ({ handleClick, number }) => {
return (
<div className="tile" onClick={() => handleClick(number)}>
tile {number}
</div>
);
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
modalOpen: false,
openedBy: ""
};
}
openModal = title => {
this.setState({
modalOpen: true,
openedBy: title
});
};
closeModal = () => {
this.setState({
modalOpen: false,
openedBy: ""
});
};
render() {
return (
<div className="App">
<div>ModalOpen = {this.state.modalOpen.toString()}</div>
<div>Opened by = {this.state.openedBy}</div>
<Section handleClick={this.openModal} />
<a href="#" onClick={this.closeModal}>
Close modal
</a>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

It seems to be working perfectly fine :
const Section = ({ handleClick }) => {
return (
<div className="section">
Section
<Tile handleClick={handleClick} number="1" />
<Tile handleClick={handleClick} number="2" />
<Tile handleClick={handleClick} number="3" />
</div>
)
}
const Tile = ({ handleClick, title }) => {
return (
<div className="tile" onClick={handleClick}>
tile {title}
</div>
)
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
modalOpen: false
};
}
openModal = event => {
console.log(event.target)
this.setState({
modalOpen: true,
openedBy: ""
})
}
closeModal = event => {
console.log(event.target)
this.setState({
modalOpen: false,
openedBy: event.target.title
})
}
render() {
return (
<div className="App">
<div>ModalOpen = {this.state.modalOpen.toString()}</div>
<div>Opened by = {this.state.openedBy}</div>
<Section handleClick={this.openModal}></Section>
<a href="#" onClick={this.closeModal}>Close modal</a>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.2/umd/react-dom.production.min.js"></script>
<div id='root'>
However I assume that you want to pass down the title of the clicked element back into the handler. If so, I recommend using a curried function, with 2 sets of parameters, and setting the title variable as the first one :
openModal = title => event => {
console.log('Opened by : ', title, event.target)
this.setState({
modalOpen: true,
openedBy: ""
})
}
Your Tile component can now indicate which title it has by calling the function the first time :
const Tile = ({ handleClick, title }) => {
return (
<div className="tile" onClick={handleClick(title)}>
tile {title}
</div>
)
};
Working example :
const Section = ({ handleClick }) => {
return (
<div className="section">
Section
<Tile handleClick={handleClick} title="1" />
<Tile handleClick={handleClick} title="2" />
<Tile handleClick={handleClick} title="3" />
</div>
)
}
const Tile = ({ handleClick, title }) => {
return (
<div className="tile" onClick={handleClick(title)}>
tile {title}
</div>
)
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
modalOpen: false
};
}
openModal = title => event => {
console.log('Opened by : ', title)
this.setState({
modalOpen: true,
openedBy: ""
})
}
closeModal = event => {
this.setState({
modalOpen: false,
openedBy: event.target.title
})
}
render() {
return (
<div className="App">
<div>ModalOpen = {this.state.modalOpen.toString()}</div>
<div>Opened by = {this.state.openedBy}</div>
<Section handleClick={this.openModal}></Section>
<a href="#" onClick={this.closeModal}>Close modal</a>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.2/umd/react-dom.production.min.js"></script>
<div id='root'>

Related

Re-render component based on object updating

I have the following pattern
class List {
list: string[] = [];
showList() {
return this.list.map(element => <div>{element}</div>);
}
showOptions() {
return (
<div>
<div onClick={() => this.addToList('value1')}>Value #1</div>
<div onClick={() => this.addToList('value2')}>Value #2</div>
<div onClick={() => this.addToList('value3')}>Value #3</div>
<div onClick={() => this.addToList('value4')}>Value #4</div>
</div>
);
}
addToList(value: string) {
this.list.push(value);
}
}
class App extends Component {
myList: List;
constructor(props: any) {
super(props);
this.myList = new List();
}
render() {
<div>
Hey this is my app
{this.myList.showOptions()}
<div>{this.myList.showList()}</div>
</div>
}
}
It shows my options fine, and elements are added to the list when I click on it. However, the showList function is never called again from App, thus not showing any update.
How can I tell the main component to rerenders when List is updated ? I'm not sure my design pattern is good. My goal is to manage what my class displays inside itself, and just call the display functions from other components.
We should always use state to rerender react component.
Not sure what you want to accomplish exactly but hopefully this will give you a general idea what Jim means with using state:
const Option = React.memo(function Option({
value,
onClick,
}) {
return <div onClick={() => onClick(value)}>{value}</div>;
});
const Options = React.memo(function Options({
options,
onClick,
}) {
return (
<div>
{options.map(value => (
<Option
key={value}
value={value}
onClick={onClick}
/>
))}
</div>
);
});
class List extends React.PureComponent {
state = {
options: [1, 2, 3],
selected: [],
};
showList() {
return this.list.map(element => <div>{element}</div>);
}
add = (
value //arrow funcion to bind this
) =>
this.setState({
options: this.state.options.filter(o => o !== value),
selected: [...this.state.selected, value],
});
remove = (
value //arrow funcion to bind this
) =>
this.setState({
selected: this.state.selected.filter(
o => o !== value
),
options: [...this.state.options, value],
});
render() {
return (
<div>
<div>
<h4>options</h4>
<Options
options={this.state.options}
onClick={this.add}
/>
</div>
<div>
<h4>choosen options</h4>
<Options
options={this.state.selected}
onClick={this.remove}
/>
</div>
</div>
);
}
}
const App = () => <List />;
//render app
ReactDOM.render(
<App />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

How to return element in react class functions

How to return element in react class functions on a click. is it even possible?
class Item extends Component {
constructor(props) {
super(props);
this.itemInfo = this.itemInfo.bind(this);
}
itemInfo = () =>{
return <div> some info</div>
}
render(){
return(
<div>
<div onClick={this.itemInfo}> Click Here <div>
</div>
)
}
}
class Item extends React.Component {
state = {
showDiv: false
};
render() {
return (
<div>
<div
style={{ cursor: "pointer" }}
onClick={() =>
this.setState(prevState => ({
showDiv: !prevState.showDiv
}))
}
>
Click Me
</div>
{/*Show the INFO DIV ONLY IF THE REQUIRED STATE IS TRUE*/}
{this.state.showDiv && <InfoDiv />}
</div>
);
}
}
//This is the div which we want on click
var InfoDiv = () => (
<div style={{ border: "2px solid blue",borderRadius:10, padding: 20 }}>
<p> Long Text DIVLong Text DIVLong Text DIVLong Text DIVLong Text DIV </p>
</div>
);
ReactDOM.render(<Item />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You should do that in the state.
itemInfo = () =>{
this.setState({ component:<div> some info</div> });
}
and render the component like this
return(
<div>
<div onClick={this.itemInfo}> Click Here <div>
{this.state.component}
</div>
)
You can try something like this, using the state and conditional rendering:
class Item extends Component {
constructor(props) {
super(props)
this.state = {
showMore: false,
}
}
toggleShowMore = () => {
this.setState({ showMore: !this.state.showMore })
}
render() {
return (
<div>
<div onClick={this.toggleShowMore}>
{this.state.showMore ? 'Show less' : 'Show more'}
</div>
{this.state.showMore ? <div>some info</div> : null}
</div>
)
}
}
Here's how I would do it:
function ItemInfo() {
return(
<div>Some Info</div>
);
}
class Item extends Component {
constructor(props) {
super(props);
this.handleClick= this.handleClick.bind(this);
this.state = {
showInfo: false
}
}
handleClick() {
this.setState((prevState) => {showInfo: !prevState.showInfo});
}
render(){
return(
<div>
<div onClick={this.handleClick}> Click Here <div>
{ this.state.showInfo ?
<ItemInfo/>
: null }
</div>
)
}
}

Getting error while closing the react-modal

here when a user click on a picture of the UserProfile component, a modal opens with details about the picture. But when a user closes the modal, an error is generated.
Error:
Cannot read property 'uid' of undefined
at t.value (PostListItem.js:68)
I think t is trying to rendering postlistItem after modal close which should not happen as the content is set to '' while closing the modal.
//UserProfile
class UserProfile extends React.Component{
constructor(props){
super(props);
this.state = {
isFollowed: false,
content: undefined
}
}
componentDidMount(){
this.props.dispatch(usersFetchData(`http://localhost:5000/api/user/
${this.props.match.params.uid}`));
(Object.keys(this.props.user).length !== 0) &&
(this.props.user.followers.includes(this.props.currentUser.uid)) &&
this.setState({isFollowed: true});
}
componentWillUnmount(){
this.props.dispatch(resetUser());
}
onFollow = () =>{
if(this.state.isFollowed){
this.props.dispatch(removeFollower(this.props.user.uid,
this.props.currentUser.uid));
this.props.dispatch(removeFollowing(this.props.currentUser.uid,
this.props.user.uid));
this.setState({isFollowed: false});
}else{
this.props.dispatch(addFollower(this.props.user.uid, this.props.currentUser.uid));
this.props.dispatch(addFollowing(this.props.currentUser.uid,this.props.user.uid));
this.setState({isFollowed: true});
}
}
openModal = (post) => {
this.setState({content:post});
console.log(this.state);
}
closeModal = () =>{
this.setState(() => ({ content: '' }));
console.log(this.state);
}
render(){
if (this.props.hasErrored) {
return <p>Sorry! There was an error loading the items</p>;
}
if (this.props.isLoading) {
return <p>Loading…</p>;
}
return(
<div className="userProfile">
<div>
{console.log(this.props.user)}
{ Object.keys(this.props.user).length !== 0 &&
<div className="user__details">
<div className="user__dp">
<div className="dp__container">
<img src={this.props.user.avatar} alt=
{this.props.user.name}/>
</div>
</div>
<div className="user__description">
<p className="user__name">
{this.props.user.name}</p>
<div className="user__button">
{(this.props.currentUser.uid ===
this.props.user.uid) ?
<button className="ef__button"><Link
to={`/${this.props.user.uid}/edit`}>Edit Profile</Link></button> :
<button
className="ef__button"
onClick={this.onFollow}>
{this.state.isFollowed? 'Following': 'Follow'}
</button>
}
</div>
</div>
</div>
}
</div>
<div className="user__bio">
<p>{this.props.user.bio}</p>
</div>
<div>
<h3>Posts</h3>
<div className="userContent">
{this.props.user.posts &&
this.props.user.posts.map((post) =>{
return(
<div className="userPost">
<img src={post.content} onClick={() =>
this.openModal(post)}/>
</div>
);
})
}
<ContentModal
content={this.state.content}
closeModal={this.closeModal}
/>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) =>{
console.log(state);
return{
currentUser: state.auth,
user: state.users,
hasErrored: state.usersHasErrored,
isLoading: state.usersIsLoading
}
};
export default connect(mapStateToProps)(UserProfile);
//contentModal
const ContentModal = (props) => (
<Modal
isOpen={!!props.content}
onRequestClose={props.closeModal}
shouldCloseOnOverlayClick={true}
shouldCloseOnEsc={true}
contentLabel="content"
closeTimeoutMS={200}
className="content__modal"
>
<div className="post__container">
{<PostListItem {...props.content}/>}
</div>
{console.log(props)}
</Modal>
You get the issue because intially the content is undefined and when you are closing the model the content is set to empty string so uid won't be available so call PostListItem only when content is not undefined and not empty.
Add the below condition in ContentModal component
{typeof props.content != "undefined" && props.content != "" && <PostListItem {...props.content}/>}

Updating props in note taking app in React

I'm stuck on my note taking app. Basically the App component passes in data to the NoteEntry component through props. Yet I can't figure out how to edit the previous passed text through props within each NoteEntry instance when I click the "edit" button. The edit button is supposed to bring up text inputs to change the content by updating the text and then pressing the save button. Any tips on how to go about it?
class App extends Component {
constructor(props) {
super(props);
this.state = {
notes: [],
title: "",
details: ""
}
this.updateTitle = this.updateTitle.bind(this);
this.updateDetails = this.updateDetails.bind(this);
this.submitHandler = this.submitHandler.bind(this);
this.deleteHandler = this.deleteHandler.bind(this);
}
updateTitle(event) {
this.setState({ title: event.target.value });
}
updateDetails(event) {
this.setState({ details: event.target.value });
}
submitHandler(e) {
e.preventDefault();
if (!this.state.title.length || !this.state.details.length) {
return;
}
const newNote = {
newTitle: this.state.title,
newDetails: this.state.details
}
this.setState(prevState => ({
notes: prevState.notes.concat(newNote),
title: "",
details: ""
}))
}
deleteHandler(id) {
this.setState(prevState => ({
notes: prevState.notes.filter(el => el !== id)
}))
}
render() {
return (
<div className="container">
<h1 className="title">React Notes App</h1>
<NoteForm
titleValue={this.state.title}
detailsValue={this.state.details}
titleHandle={this.updateTitle}
detailsHandle={this.updateDetails}
onSubmit={this.submitHandler}
/>
<div className="entry-section">
{this.state.notes.map((note, i) => (
<NoteEntry
key={i}
title={note.newTitle}
details={note.newDetails}
deleteNote={this.deleteHandler.bind(this, note)}
/>
))}
</div>
</div>
);
}
}
const NoteForm = (props) => {
return (
<div>
<form className="form-section">
<input
className="title-input"
type="type"
placeholder="Title"
value={props.titleValue}
onChange={props.titleHandle}
/>
<br />
<textarea
className="details-input"
cols="20"
rows="3"
placeholder="Details"
value={props.detailsValue}
onChange={props.detailsHandle}
/>
<br />
<button
className="input-button"
onClick={props.onSubmit}
>Add Note</button>
</form>
</div>
)
}
class NoteEntry extends Component {
constructor(props) {
super(props);
this.state = {
display: false,
editTitle: this.props.title,
editDetails: this.props.details,
editing: false
}
this.displayToggle = this.displayToggle.bind(this);
this.edit = this.edit.bind(this);
this.save = this.save.bind(this);
}
displayToggle() {
this.setState(prevState => ({
display: !prevState.display
}))
}
edit() {
this.setState({
editing: true
})
}
save() {
let titleVal = this.refs.updateTitle.value;
let detailsVal = this.refs.updateDetails.value;
this.setState({
editTitle: titleVal,
editDetails: detailsVal,
editing: false
})
}
render() {
return (
<div className="entry">
<div className="entry-header" onClick={this.state.editing ? null : this.displayToggle}>
{this.state.editing ? (
<input ref="updateTitle" className="edit-title" type="text" />
) : (
<h2 className="entry-title">{this.props.title}</h2>
)}
<p className="timestamp">{this.displayTime}</p>
</div>
<hr />
<div className={"entry-content " + (!this.state.display ? "hide-details" : null)}>
{this.state.editing ? (
<textarea ref="updateDetails" className="edit-details" cols="10" rows="2"></textarea>
) : (
<p className="details">{this.props.details}</p>
)}
<div className="entry-buttons">
{this.state.editing ? (
<button className="save" onClick={this.save}>Save</button>
) : (
<button className="edit" onClick={this.edit}>Edit</button>
)
}
<button className="delete" onClick={this.props.deleteNote}>Delete</button>
</div>
</div>
</div>
)
}
}
You can do by pass data from child to parent component as mention it in comment.
In you case NoteEntry add onEditNote props. This props use for function by parent (App component) and use by onClick edit button.
<NoteEntry
...
onEditNote={this.handleClickEdit}
/>
then in class NoteEntry
<button className="edit" onClick={() => this.props.handleClickEdit(this.props.title, this.props.detail)}>Edit</button>
So, handleClickEdit handle by App component and set it to your state
handleClickEdit = (_title, _detail) => {
this.setState({title: _title, details: _detail});
}
Now, your NoteForm component able to edit.

Highlight item onClick - React.js

Add underscore to category-item onClick and remove underscore for any other item. Found some answers on how to do this with only two components, a "item-container-component" and "item-components". But i have three components involved. This is what I hope to achieve:
Archive-component (mother component):
class Archive extends React.Component {
constructor(props){
super(props);
this.state = {
products: [],
category: "",
activeIndex: 0
}
this.filterHandler = this.filterHandler.bind(this);
}
filterHandler(tag, index){
console.log('INDEX: ' + index);
this.setState({
category: tag,
activeIndex: index
})
}
componentDidMount(){
const myInit = {
method: "GET",
headers: {
"Content-Type": "application/json"
}
};
fetch("/getProducts", myInit)
.then((res) => {
return res.json();
})
.then((data) => {
this.setState({products:data});
})
.catch(function(err) {
console.log('ERROR!!! ' + err.message);
});
}
render() {
return (
<div>
<Menu />
<div className="archive-container">
<div className="archive-wrapper">
<CategoryContainer
filterHandler={this.filterHandler}
products={this.state.products}
activeIndex={this.state.activeIndex}
/>
<br/><br/>
<ProductContainer
products={this.state.category.length
? this.state.products.filter((prod) => prod.category === this.state.category)
: this.state.products.filter((prod) => prod.category === 'Paint')
}
/>
</div>
</div>
<Footer />
</div>
);
};
};
Category-container component:
class CategoryContainer extends Component {
render(){
const categories = [...new Set(this.props.products.map(cat => cat.category))];
return (
<div>
<ul className="filterList">{
categories.map((cat, index) =>
<CategoryItem
key={index}
index={index}
category={cat}
active={index === this.props.activeIndex}
handleClick={() => this.props.filterHandler(cat, index)}
/>
)
}</ul>
</div>
);
};
};
Category-item component:
class CategoryItem extends Component {
render(){
return (
<div>
<li
className={this.props.active ? 'active' : 'category'}
onClick={this.props.handleClick}
>
{this.props.category}
</li>
</div>
);
};
};
Yelp!
M
Suppose you have a li tag for which you want to change the color of.
you could probably try something like this.
<li id="colorChangeOnClick" class="redColor" onclick={this.onClickFunction()}></li>
Then in your react class you can have the on click function with parameters e:
onClick(e) {
//This would give you all the field of the target
console.log(e.target.elements);
// you can do all sorts of Css change by this way
e.target.element.class="newGreenColor";
}
Also make sure to make a state or a prop change otherwise the page would not render again.

Resources