react stateless component with onClick issue - reactjs

I have a stateless component and i want to be able to click on the image and redirect it to a new page.
The issue that i am having is that i cannot get onClick to work correctly.
I was trying to write a function within the onClick = {this.ProjectSelected} but this will not work.
Do i have to pass a function (onClick) from the parent to child component? If so how do i do that?
I am able to log the project id to the console.
Thanks for the help.
const projectListTemplates = (props) => {
const { classes } = props;
return (
<div>
<div className={classes.root}>
<GridList className={classes.gridList}>
<GridListTile key="Subheader" cols={2} style={{ height: 'auto' }}>
<Subheader component="div">Projects</Subheader>
</GridListTile>
{props.data.map(item => (
<GridListTile key={item.image}>
<img src="./images/Project/1.jpg" alt={item.title} />
<GridListTileBar
title={item.title}
subtitle={<span> {item.name}</span>}
onClick={()=>console.log(`this project was clicked ${item.id}`)}
>
</GridListTileBar>
/>
<ProgressBar now={60} />
</GridListTile>
))}
</GridList>
</div>
</div>
);

In stateless component we are not defining any local property like state or methods. We have only props and rendering data based on props. We have only props events to trigger. So we have to pass a event function from parent component in order to handle click event. If you want to keep click event locally, convert component to stateful (Class).
Stateless
const ProjectListComponent = (props) => {
const { onClick } = props
return (
<button onClick={onClick}>Click me</button>
)
}
class AppComponent extends Component {
handleClick() {
console.log('clicked')
}
render() {
return (
<ProjectListComponent onClick={this.handleClick} />
)
}
}
Stateful
class ProjectListComponent extends Component {
handleClick() {
console.log('clicked')
}
render() {
return (
<button onClick={this.handleClick}>Click me</button>
)
}
}

Related

How to pass a value for useState hook from another component in ReactJS?

I have two components:
Parent.js
import { useState } from "react";
function Parent() {
const [showHideContent, setShowHideContent] = useState("none");
return (
<div style={{ display: showHideContent }}>
Some content here...
</div>
);
}
Child.js
function Child() {
return (
<button>
Show/Hide Content
</button>
);
}
I want to pass two values none and block (one value at a time) through setShowHideContent of Parent component using Show/Hide Content button of Child component.
How to do this?
NOTE: These two components are saved in the same folder but in two different files.
These two component are rendered by App.js.
<Route path="/content">
<Menu /> {/* rendering in LEFT */}
<div className="content-flexbox">
<Parent /> {/* rendering in CENTER */}
<Child /> {/* rendering in RIGHT */}
</div>
<Footer /> {/* rendering in BOTTOM */}
</Route>
It seems like you want the Child component to simply toggle the display value of some content in the Parent component.
As you've defined them though they are not parent-child, but rather they are siblings. As such if they need to share state/behavior, then the solution is to lift state up to a common ancestor, App in this case.
<Route path="/content">
<Menu />
<div className="content-flexbox">
<Parent /> // <-- siblings
<Child /> // <-- siblings
</div>
<Footer />
</Route>
See: Lifting State Up
Move the showHideContent state and updater into the parent App component, pass down the showHideContent state to Parent and the toggleVisibility callback to the Child.
function Parent({ showHideContent }) {
return <div style={{ display: showHideContent }}>Some content here...</div>;
}
function Child({ onClick }) {
return (
<button type="button" onClick={onClick}>
Show/Hide Content
</button>
);
}
export default function App() {
const [showHideContent, setShowHideContent] = React.useState("none");
const toggleVisibility = () =>
setShowHideContent((value) => (value === "none" ? "block" : "none"));
return (
...
<Route path="/content">
<Menu />
<div className="content-flexbox">
<Parent showHideContent={showHideContent} />
<Child onClick={toggleVisibility} />
</div>
<Footer />
</Route>
...
);
}
You can simply pass values as prop to child component.
Call your child component inside parent component and pass value
import { useState } from "react";
import Child from "//your path";
function Parent() {
const [showHideContent, setShowHideContent] = useState("none");
return (
<div style={{ display: showHideContent }}>
<Child showHide={showHideContent}/>
</div>
);
}
Now in your child component
function Child({showHide}) {
return (
<button>
{showHide}
Show/Hide Content
</button>
);
}
Include your Child component in your Parent component and pass hooks as props
import { useState } from "react";
// import the child component
function Parent() {
const [showHideContent, setShowHideContent] = useState("none");
return (
<div style={{ display: showHideContent }}>
Some content here...
<Child showHideContent={showHideContent}/>
</div>
);
}
And use them in your child components
function Child({showHideContent}) {
return (
<button>
{showHideContent}
Show/Hide Content
</button>
);
}
Another way of doing the same is by using Context API:
https://reactjs.org/docs/hooks-reference.html#usecontext
You can pass an inline function as prop from parent to child like this.
function Parent(){
const [showHideContent, setShowHideContent] = useState("none");
return (
<Child onButtonClicked={(toValue) => setShowHideContent(toValue)} />
)
}
function Child({ onButtonClicked }) {
return (
<button onClick={onButtonClicked} >
Show/Hide Content
</button>
);
}
Pass down exact state modifier with same name
function Parent(){
const [showHideContent, setShowHideContent] = useState("none");
return (
<Child setShowHideContent={setShowHideContent} />
)
}
//or with prop speading
function Parent(){
const [showHideContent, setShowHideContent] = useState("none");
return (
<Child {...{setShowHideContent}} />
)
}

Patterns in React (wrapper)

Good day. I'm building a tree of components and want to use functions of root component in other components of tree. I throw function reference through all tree.
Also I use the object if me need get value from the function in not root componet.
Can you help me?
Can you show me how to do this as HOC ?
If it will be not so hard for you show examples on my code.
import React from 'react';
class Page extends React.Component{
Answer = {
value : ''
}
doSomething(){
console.log(this.Answer.value);
console.log('Ready!');
}
render(){
return(
<div>
<div>
<Body
ParentFunc={()=>this.doSomething()}
ParentParameters={this.Answer}
/>
</div>
</div>
)
}
}
export default Page
class Body extends React.Component{
render(){
const{
ParentFunc,
ParentParameters
} = this.props
return(
<div>
<div>
<SomeComponent
ParentFunc={()=>ParentFunc()}
ParentParameters={ParentParameters}
/>
</div>
</div>
)
}
}
class SomeComponent extends React.Component{
getAnswer(){
const{
ParentFunc,
ParentParameters
} = this.props
ParentParameters.value = 'Some text'
ParentFunc()
}
render(){
return(
<div onClick={()=>this.getAnswer()}>
We can?
</div>
)
}
}
I don't believe a Higher Order Component alone will solve your basic issue of prop drilling. A React Context would be a better fit for providing values and functions generally to "want to use functions of root component in other components of tree".
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
In a typical React application, data is passed top-down (parent to
child) via props, but such usage can be cumbersome for certain types
of props (e.g. locale preference, UI theme) that are required by many
components within an application. Context provides a way to share
values like these between components without having to explicitly pass
a prop through every level of the tree.
Start by creating your Context and Provider component:
const QnAContext = React.createContext({
answer: {
value: ""
},
doSomething: () => {}
});
const QnAProvider = ({ children }) => {
const answer = {
value: ""
};
const doSomething = () => {
console.log(answer.value);
console.log("Ready!");
};
return (
<QnAContext.Provider value={{ answer, doSomething }}>
{children}
</QnAContext.Provider>
);
};
Render QnAProvider in your app somewhere wrapping the React subtree you want to have access to the values being provided:
<QnAProvider>
<Page />
</QnAProvider>
Consuming the Context:
Class-based components consume contexts via the render props pattern.
<QnAContext.Consumer>
{({ answer, doSomething }) => (
<SomeComponent doSomething={doSomething} answer={answer}>
We can?
</SomeComponent>
)}
</QnAContext.Consumer>
Functional components can use the useContext React hook
const SomeComponent = ({ children }) => {
const { answer, doSomething } = useContext(QnAContext);
getAnswer = () => {
answer.value = "Some text";
doSomething();
};
return <div onClick={this.getAnswer}>{children}</div>;
};
Here is where using a Higher Order Component may become useful. You can abstract the QnAContext.Consumer render props pattern into a HOC:
const withQnAContext = (Component) => (props) => (
<QnAContext.Consumer>
{(value) => <Component {...props} {...value} />}
</QnAContext.Consumer>
);
Then you can decorate components you want to have the context values injected into.
const DecoratedSomeComponent = withQnAContext(SomeComponent);
...
<DecoratedSomeComponent>We can with HOC?</DecoratedSomeComponent>
Note: The point of doing all this was to move the values and functions that were previously defined in Page into the Context, so they are no longer passed from Page though Body to SomeComponent (or any other children components).
Demo
Sandbox Code:
const QnAContext = React.createContext({
answer: {
value: ""
},
doSomething: () => {}
});
const QnAProvider = ({ children }) => {
const answer = {
value: ""
};
const doSomething = () => {
console.log(answer.value);
console.log("Ready!");
};
return (
<QnAContext.Provider value={{ answer, doSomething }}>
{children}
</QnAContext.Provider>
);
};
const withQnAContext = (Component) => (props) => (
<QnAContext.Consumer>
{(value) => <Component {...props} {...value} />}
</QnAContext.Consumer>
);
class SomeComponent extends React.Component {
getAnswer = () => {
const { doSomething, answer } = this.props;
answer.value = "Some text";
doSomething();
};
render() {
return (
<button type="button" onClick={this.getAnswer}>
{this.props.children}
</button>
);
}
}
const DecoratedSomeComponent = withQnAContext(SomeComponent);
class Body extends React.Component {
render() {
return (
<div>
<div>
<QnAContext.Consumer>
{({ answer, doSomething }) => (
<SomeComponent doSomething={doSomething} answer={answer}>
We can?
</SomeComponent>
)}
</QnAContext.Consumer>
</div>
<div>
<DecoratedSomeComponent>We can with HOC?</DecoratedSomeComponent>
</div>
</div>
);
}
}
class Page extends React.Component {
render() {
return (
<div>
<div>
<Body />
</div>
</div>
);
}
}
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<QnAProvider>
<Page />
</QnAProvider>
</div>
);
}
Based on your current code I am making the assumption that Body does not modify the values of ParentFunc and ParentParameters before passing them down to SomeComponent.
You have a hierarchy
<Page>
<Body>
<SomeComponent>
</Body>
</Page>
and you want to pass props from Page to SomeComponent without going through Body.
You can do this using children
children is a special prop representing the JSX child elements of the component. We make it so that Body renders the children that it got through props:
class Body extends React.Component{
render() {
return(
<div>
<div>
{this.props.children}
</div>
</div>
)
}
}
We set that children prop by using a <SomeComponent/> element inside of the <Body>:
render() {
return (
<div>
<div>
<Body>
<SomeComponent
ParentFunc={() => this.doSomething()}
ParentParameters={this.Answer}
/>
</Body>
</div>
</div>
);
}
Note that you cannot directly modify the value that you got from the parent, which is what you were doing with ParentParameters.value = 'Some text'. If you want to update the state of the parent then you need to do that through your callback function props. So your code should look something like this:
import React from "react";
class Body extends React.Component {
render() {
return (
<div>
<div>{this.props.children}</div>
</div>
);
}
}
class SomeComponent extends React.Component {
state = {
showAnswer: false
};
onClick() {
// update answer in parent
this.props.setAnswer("Some text");
// change state to reveal answer
this.setState({ showAnswer: true });
}
render() {
return (
<div>
{this.state.showAnswer && <div>Answer is: {this.props.answer}</div>}
<div onClick={() => this.onClick()}>We can?</div>
</div>
);
}
}
class Page extends React.Component {
state = {
value: ""
};
render() {
return (
<div>
<div>
<Body>
<SomeComponent
answer={this.state.value}
setAnswer={(answer) => this.setState({ value: answer })}
/>
</Body>
</div>
</div>
);
}
}
export default Page;

how to disable a button in React imported into another component?

it's my first application in react and I'm not sure how to disable an imported button.
I have a component button that I import into a parent component
import React, { Component } from "react";
import "../../index.scss";
class Submit extends Component {
render() {
return (
<button className="button"
onClick={() => this.props.onClick()}>
SUBMIT
</button>
);
}
}
export default Submit;
in the component that rendered it is as follows
renderSubmit() {
return (
<Submit
onClick={() => this.submitForm()}
/>
);
}
render() {
return (
<div className="table">
<div className="table-actions">
{this.renderRefresh()}
{this.renderSubmit()}
</div>
</div>
);
}
}
I have tried to set the class to disabled from the original component but it depends on a state property and does not recognize it.
import React, { Component } from "react";
import "../../index.scss";
class Submit extends Component {
render() {
return (
<button className="button"
disabled={this.state.start}
onClick={() => this.props.onClick()}>
SUBMIT
</button>
);
}
}
export default Submit;
How can I condition the disabled state to a state property?
Your Submit button doesn't allow for setting any other props on the underlying button component. It should proxy though any props you want to be externally configured by what is rendering the Submit button. I also suggest explicitly declaring the button type to be "submit", or also exposing that prop out in the component API.
Your proxying of the onClick handler also drops the click event, that should be passed through in case any consuming component care about it.
class Submit extends Component {
render() {
const { disabled, onClick, type = "submit" } = this.props;
return (
<button
className="button"
disabled={disabled}
onClick={onClick}
type={type}
>
SUBMIT
</button>
);
}
}
For such a simple component with no internal logic IMO a functional component is a better option, and I would name it more clearly.
const SubmitButton = ({ disabled, onClick, type = "submit" }) => (
<button
className="button"
disabled={disabled}
onClick={onClick}
type={type}
>
SUBMIT
</button>
);
Now when you are using the submit button from a parent component you can pass in a disabled prop based on any condition you need/require.
render() {
const { submitDisabled } = this.state;
return (
<div className="table">
<div className="table-actions">
{this.renderRefresh()}
<SubmitButton
disabled={submitDisabled} // <-- pass disabled value
onClick={this.submitForm} // <-- attach click handler
type="button" // <-- so we don't accidentally take default form action
/>
</div>
</div>
);
}
}
How you compute/set this.state.submitDisabled is up to you. Maybe it is disabled when the form is being submitted, for example.
submitForm = () => {
this.setState({ submitDisabled: true });
...
};

reactjs conditional render works after second click

I have three components: MyDashboard, MyContent, MyForm.
The MyDashboard component starts off by rendering the MyContent component as a child. I have a button within the MyContent component which when clicked should swap out the component with the MyForm component.
Clicking the button flashes the MyForm component on the first click, then goes straight back to the MyContent component. After the second click however the MyForm component is rendered and stays rendered.
MyDashboard:
function MyDashboard(props) {
const [viewForm, setViewForm] = useState(false); // Don't show the form initially
const handleClick = () => {
setViewForm(!viewForm);
};
return (
<div>
{(() => {
if (viewForm) { // conditionally render the form or the content
return <MyForm {...props} />;
} else {
return (
<MyContent
{...props}
setViewForm={handleClick}
viewForm={viewForm}
/>
);
}
})()}
</div>
);
}
MyContent:
function MyContent(props) {
return (
<p>
<a href="#" onClick={props.setViewForm}> // need to click this twice to render the form.
Show the form
</a>
</p>
);
}
export default MyContent;
MyForm:
function MyForm({ props, location }) {
// form setup
return (
<div>
<Row>
<Form>
//... the form
</form>
</Row>
</div>
);
}
export default MyForm;

How do I pass up the value inside of react-autosuggest to the parent component?

In my app.js, I have this
render() {
return (
<div id="App">
<SearchBar />
</div>
);
}
And inside of SearchBar, I import react-autosuggest and have this -
render() {
const { value, suggestions } = this.state;
const inputProps = {
placeholder: "Search",
value,
onChange: this.onChange
};
return (
<Autosuggest
style={style}
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
onSubmit={this.doSomething}
/>
);
}
Where all these functions are standard boilerplate functions that react-autosuggest uses. How do I access what was searched for inside of SearchBar inside of it's parent, app.js?
You can use props to raise the data from the Autosuggest events to the parent component. Create a method inside App and pass it down as a prop to the SearchBar component. Then, call it with the Autosuggest event.
App
class App extends React.Component {
onSubmit(e) {
// `e` is the data that was passed through from the `Autosuggest` event.
console.log(e);
}
render() {
return (
<div id="App">
<SearchBar onSubmit={this.onSubmit} />
</div>
);
}
}
SearchBar
<Autosuggest onClick={this.props.onSubmit} />
You can use a callback function at the SearchBar component. Follow the steps below according to your use case:
Add below function at SearchBar component
onHandleChange(event, value){
this.setState({ value });
this.props.getInputData(event, value);
}
Use above function at AutoSuggest component
return (
<Autosuggest
...
onChange={(event, value) => this.onHandleChange(event, value)}
...
/>
);
Use SearchBar component like
render() {
return (
<div id="App">
<SearchBar getInputData={(event, value) => this.getAutosuggestInput(event, value)}/>
</div>
);
}
Here you get the value of input box at the parent component
getAutosuggestInput = (event, value) => {
//add your logic here
}

Resources