I want to render my data after button click. I use the condition rendering in my component and create a boolean variable in state object. After button clicked variable is changed and (as I expected) my data was renderered. But nothing happens. I know that this is a basic mistake.
class SortingMyArray extends React.Component {
constructor(props) {
super(props)
this.state = {
addcomments: addcomments,
isClicked : false
}
// this.sortBy = this.sortBy.bind(this)
}
sortBy() {
let sortedComments = [...this.state.addcomments].sort((a,b) => new Date(b.date) - new Date(a.date));
this.setState({
addcomments: sortedComments,
isClicked : true
})
console.log(this.state.isClicked)
}
render(){
return(
<div>
<div>
<button
onClick={() => this.sortBy() }>AdditionalCommentaries
</button>
</div>
</div>
)
if (this.state.isClicked){
return(
<div>
<div>
<AddComments addcomments = {this.state.addcomments} />
</div>
</div>
)
}
}
}
export default SortingMyArray;
You need to restructure your render method since part of code is unreachable, also you don't need to use IF sentence, you can use logical operator && to ask if isClicked is true to return AddComments component.
You need to install eslinter in your code editor, this can you help to detect this type
errors or others
RENDER METHOD:
render() {
//You can destructuring object, in this case your state is the object
const { isClicked, addcomments } = this.state;
return (
<div>
<div>
<button onClick={() => this.sortBy()}>AdditionalCommentaries</button>
</div>
{isClicked && (
<div>
<AddComments addcomments={addcomments} />
</div>
)}
</div>
);
}
You should not have more than one return inside render method. Instead try this:
Note: There is no if-else statement in JSX but we use the one bellow
render(){
return(
<div>
<div>
<button
onClick={() => this.sortBy() }>AdditionalCommentaries
</button>
</div>
</div>
{
this.state.isClicked
&&
<div>
<div>
<AddComments addcomments = {this.state.addcomments} />
</div>
</div>
}
)
}
This part of your code is unreachable by the program since it is placed after return. You should not have two returns.
if (this.state.isClicked){
return (
<div>
<div>
<AddComments
addcomments={this.state.addcomments}
/>
</div>
</div>
)
}
Also, your conditional statement is not valid JSX. Your render method should look something like this
render() {
return (
<div>
<div>
<button onClick={() => this.sortBy()}>
AdditionalCommentaries
</button>
</div>
{this.state.isClicked && (
<div>
<div>
<AddComments
addcomment={this.state.addcomments}
/>
</div>
</div>
)}
</div>
)
}
Related
I have an array of objects that I am looping through in my higher order component. On click I want to pass one of those objects to my component and render that component. The problem I am having is it's unclear how to return the component and have React update the render to show that component. I've read several Stackoverflow posts on this and the problem of putting the component in the html is it passes all the items in my loop to my component instead of just the one I need onClick.
The warning I'm seeing in my console is react_devtools_backend.js:3973 Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> Which makes sense however, I'm not sure what the proper syntax is to solve this problem. Below is the relevant code.
const FormGroup = ({index}) => {
const renderSection = form => ( // I want to render this
<AdditiveSection
form={form}
register={register}
errors={errors}
/>
);
const addSection = form => {
setAdditionalSection(prevState => !prevState);
console.log('form', form);
renderSection(form);
};
return (
<section>
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
{myForm.controls.map(form => {
if (form.type === 'section') {
return (
<FormSection>
<div className="section__label">
<h4>{form.label}</h4>
</div>
...
{form.button
&& (
<FormAdd>
<LinkButton
type="button"
onClick={() => addSection(form)} // called here and passes my form object
>
<span className="button--small">{form.button}</span>
</LinkButton>
</FormAdd>
)}
{renderFormType(form)}
</FormSection>
);
}
})}
// renderSection should render here outside of the loop
{additionalSection && renderSection}
<input type="submit" />
</form>
</FormProvider>
</section>
);
Put it in state and just render.
const { extraSection, setExtraSection } = useState(null);
const addSection = form => {
setExtraSection(form);
};
return (
...
// renderSection should render here outside of the loop
{extraSection}
);
The problem with your original approach is that you were not saving that element anywhere. You called renderSection upon click, but that just called the function without storing or displaying that code anywhere.
Then in you render method, you again referenced rederSection. This is just a copy of the function, now without a parameter. It doesn't have any element as parameter, and it's not even called, so React is complaining that you're trying to render a function, instead of an element.
try it
const renderSection = form => (
return(
<AdditiveSection
form={form}
register={register}
errors={errors}
/>
)
);
Just in case anyone else may be struggling with this. Thanks to szaman and this article. Turns out I need to pass the section in the loop, but just pass through the data that was clicked.
const addSection = form => {
console.log('form', form);
setAdditionalSection(form);
};
return (
<section>
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
{myForm.controls.map(form => {
if (form.type === 'section') {
return (
<FormSection>
<div className="section__label">
<h4>{form.label}</h4>
</div>
...
{form.button
&& (
<FormAdd>
<LinkButton
type="button"
onClick={() => addSection(form)} // called here and passes my form object
>
<span className="button--small">{form.button}</span>
</LinkButton>
</FormAdd>
)}
{additionalSection && additionalSection.position === ind && (
<AdditiveSection
form={additionalSection}
register={register}
errors={errors}
/>
)}
{renderFormType(form)}
</FormSection>
);
}
})}
<input type="submit" />
</form>
</FormProvider>
</section>
);
I am creating a "presentation" component with multiple sections, each rendered dynamically.
In the parent component which houses all the different children, I want the "next button" disabled for each part until a certain condition has been met. The button lives in the parent component.
This component does not pass the property:
Child one example:
export function ChildOne() {
const [condition, setCondition] = useState(false);
return (
<div>
<button onClick={() => setCondition(true)}>
hello world
</button>
</div>
);
}
Parent:
import ChildOne, condition from "../child-one"
export default function Parent() {
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
I'm not sure how to pass the condition property from the child component so that I can use it in the parent component. In addition, is this methodology an anti-pattern? Can I conditionally make the button in the parent disabled based on values from the child component in another way?
Thank you.
try this way
child:
export function ChildOne({setCondition}) {
return (
<div>
<button onClick={() => setCondition(true)}>
hello world
</button>
</div>
);
}
Parent:
import {ChildOne} from "../child-one"
export default function Parent() {
const [condition, setCondition] = useState(false);
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne setCondition={setCondition} />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
You should use a state in parent component to control disabled for steps. It can use when you have other pages.
export default function Parent() {
const [condition, setCondition] = useState({});
const changeCondition = (val) => {
setCondition({...condition, [page]: val})
}
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne changeCondition={} />
)}
</div>
<button isDisabled={!condition[page]}>Next</button>
);
}
export function ChildOne({changeCondition}) {
return (
<div>
<button onClick={() => {changeCondition(true)}}>
hello world
</button>
</div>
);
}
You could pass the onClick fucntion as a props param.
Child
export function ChildOne({onClick}) {
return (
<div>
<button onClick={onClick}>
hello world
</button>
</div>
);
}
Parent
import ChildOne, condition from "../child-one"
export default function Parent() {
const [condition, setCondition] = useState(false);
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne onClick={() => setCondition(true)} />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
in your Parent component try this :
import ChildOne, condition from "../child-one"
export default function Parent() {
const [condition, setCondition] = useState(false);
const handleClick = () => setCondition(true)
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne handleClick={handleClick} />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
and in use children :
export function ChildOne({handleClick}) {
return (
<div>
<button onClick={handleClick}>
hello world
</button>
</div>
);
}
Is there a way to achieve conditionally rendered content below but instead of using {renderAuthButton()} in the return statement, I want to achieve running renderAuthButton() with onCLick instead?
class App extends Component {
// ...
render() {
let {isLoggedIn} = this.state;
const renderAuthButton = () => {
if (isLoggedIn) {
return <button>Logout</button>;
} else {
return <button>Login</button>;
}
}
return (
<div className="App">
<h1>
This is a Demo showing several ways to implement Conditional Rendering in React.
</h1>
{renderAuthButton()}
</div>
);
}
}
I don't really understand your need but to render conditionally, you can do something like that
state = {
show: false,
}
<div className="App">
<button onClick={() => this.setState((prev) => { show: !prev.show })}>Toggle</button>
{this.state.show && <MyComponent />}
</div>
I'm not completely sure what you're trying to do but this is how you would conditionally render content in react:
class App extends React.Component {
constructor(props){
super(props);
this.state = {
show: false
}
this.toggleShow = this.toggleShow.bind(this);
}
toggleShow(){
this.setState({show: ! this.state.show})
}
render(){
return (
<div>
<button onClick={this.toggleShow}>Filter Content</button>
{this.state.show ? (
<p>This content is conditionally shown</p>
) : (
<p>The content is now hidden</p>
)}
</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>
I'm trying to pass id from child component(and nested component) to it's parent.
var Comment = React.createClass({
handleClick: function(id){
console.log(this.props, id)
this.props.handleClick(this.props.comment.id)
},
render: function() {
var comment = this.props.comment
return <div className="Comment">
<div onClick={()=> this.handleClick(comment.id)} dangerouslySetInnerHTML={{__html: comment.comment_text}}/>
{comment.children.length > 0 && comment.children.map(function(child) {
return <Comment key={child.id} comment={child}/>
})}
</div>
}
})
but the function in child it's undefined and also not to make function availble in nested child.
https://jsfiddle.net/yk5nomzb/1/
Any help would be appreciate it
I made it work by changing the function into an arrow function inside the App.js render like this:
render() {
return (
<div>
{this.props.comments.map(comment => {
return (
<Comment key={comment.id}
handleClick={this.handleClick}
comment={comment}
/>
);
})}
</div>
);
}
Also in the Comment component, you need to add handleClick prop to the child Comment components like this:
render() {
var comment = this.props.comment;
return (
<div className="Comment">
<div
onClick={() => this.handleClick(comment.id)}
dangerouslySetInnerHTML={{ __html: comment.comment_text }}
/>
{comment.children.length > 0 &&
comment.children.map(child => {
return (
<Comment
key={child.id}
handleClick={this.props.handleClick}
comment={child}
/>
);
})}
</div>
);
}
So the problem is likely the famous this and bind issue in javascript.
Codesandbox
I'm trying to pass two functions yet not working. Here is my attempt
ChildComponent.js
render(
let val = -1
return(
<div>
<Button onClick={this.props.handelSecondClick} >second button click</Button>
<Button onClick={this.props.handelFirstClick} >First button click</Button>
</div>
)
)
App.js
this.state = {
data: [1,2]
}
handelFirstClick(val){
alert("first button clicked")
let data = this.state.data
data[0] = val
this.setState({data})
}
handelSecondClick(val){
alert("second button clicked")
let data = this.state.data
data[1] = val
this.setState({data})
}
render(
return(
< ChildComponent onClick={this.handelSecondClick(val)} onClick={this.handelFirstClick(val)}
)
/>
)
Is it even possible to pass two functions? On child component, I am creating two buttons two handle the two methods separetly. How should I modify so that both functions are passed two the child component?
Create 2 different functions.
Also you have missed out on binding these functions. Either use arrow functions or bind method.
< ChildComponent value={val}
handleSecondClick={() => this.handelSecondClick(val)}
handleFirstClick={()=> this.handelFirstClick(val)}
/>
You can access it via
this.props.handleSecondClick(this.props.value)
this.props.handleFirstClick(this.props.value)
Easy fix would be
ChildComponent.js
render(
return(
<div>
<Button onClick={this.props.onFirstClick} >second button click</Button>
<Button onClick={this.props.onSecondClick} >First button click</Button>
</div>
)
)
App.js
this.state = {
data: [1,2]
}
handelFirstClick(){
alert("first button clicked")
let data = this.state.data
data[0] = -1
this.setState({data})
}
handelSecondClick(){
alert("second button clicked")
let data = this.state.data
data[1] = -1
this.setState({data})
}
render(
return(
< ChildComponent onSecondClick={this.handelSecondClick} onFirstClick={this.handelFirstClick}
)
/>
)
Also this is recommended to do a check on the onclick, if the props is actually function or not, otherwise it will throw an error.
Try
onFirstClick = () => {
const {onFirstClick } = this.props;
if (typeof onFirstClick === 'function') {
onFirstClick();
}
}
render(
return(
<div>
<Button onClick={this.onFirstClick} >second button click</Button>
<Button onClick={this.onSecondClick} >First button click</Button>
</div>
)
)
You have to send 2 different props for handling multiple clicks. Do something like
<ChildComponent firstClick={this.handleFirstClick.bind(this, val)} secondClick={this.handleSecondClick.bind(this, val)} />
handleFirstClick(val){...}
handleSecondClick(val){...}
In your child component, access them as
render(){
return(
<Button onClick={this.props.firstClick}>Button 1</Button>
<Button onClick={this.props.secondClick}>Button 2</Button>
)
}
You should you object-destructuring
App.js
render() {
return(
< ChildComponent func1={this.handelSecondClick} func2={this.handelFirstClick} />
)
}
ChildComponent .js
render() {
const {func1, func2} = this.props
return(
<Button onClick={() => func1(val)} >second button click</Button>
<Button onClick={() => func2(val)} >First button click</Button>
)
}
There are two buttons in the component, you can pass both the functions as props in the component and add onClick method on buttons like:-
return(
<div>
<Button onClick={this.props.function2} >second button click</Button>
<Button onClick={this.props.function1} >First button click</Button>
</div>
)
and then in the component you can write as:-
< ChildComponent function1={this.handelFirstClick()} function2={this.handelSecondClick()} />
Hope this helps!