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
Related
I have question regarding sending clicked value to the first sibling components and I will send the props value to the second siblings is that possible?
I have module where when I click the Category Items to the first siblings the Products on the second sibling will change based on the category name that I click on the first sibling.
Here is my Parent Components:
function BuyerCategoryPage(props) {
return (
<div>
<CategorySlider />
<CategoryProducts/>
</div>
)
}
export default BuyerCategoryPage
First Sibling CategorySlider.js:
const HandleChangeProductCat = (value) => {
console.log(value);
// send the value to the second sibling
}
return (
<div>
<Container fluid>
<Slider style={{paddingTop:20, margin: '0 auto'}} {...settings}>
{
SlideData.map((data) => {
return (
<div key={data.id} >
<div className="sliderDataCategory">
<h6>
<Container>
<Row>
<Col md={3}>
<img className="img-fluid" src={data.cat_image} />
</Col>
<Col >
<label onClick={() => HandleChangeProductCat(data.cat_title)}>{data.cat_title}</label>
</Col>
</Row>
</Container>
</h6>
</div>
</div>
)
})
}
</Slider>
</Container>
</div>
)
Second Sibling CategoryProducts.js
function CategoryProducts(props) {
}
The simplest would be to lift state up into the BuyerCategoryPage component and pass an "onChange" handler to CategorySlider and the state value to CategoryProducts
function BuyerCategoryPage(props) {
const [state, setState] = React.useState(); // common state
return (
<div>
<CategorySlider onChange={setState} /> // pass updater function
<CategoryProducts value={state} /> // pass state value
</div>
);
}
CategorySlider
const handleChangeProductCat = (value) => {
console.log(value);
props.onChange(value); // invoke callback and pass new value
}
CategoryProducts
function CategoryProducts({ value }) { // access the passed value
// use the `value` prop as necessary
}
For example:
function Example() {
const [value, setValue] = useState("");
const onClick = (someValue) => {
setValue(someValue);
}
return (
<div>
<First onClick={onClick} />
<Second value={value} />
</div>
)
}
function First(props) {
const onClick = () => {
props.onClick((new Date()).toString());
}
return (
<input type="button" onClick={onClick} value="Click me" />
)
}
function Second(props) {
return (
<div>{props.value}</div>
)
}
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>
);
}
So I want when I press the button in the Button Component everything in the 'li section' disappears as well as in the ImageComponent but it not working I would like to know what my mistake is. ButtonComponent is rendered somewhere else.
App Component/Parent
function App({ hideButton }) {
return (
<div className="App">
<ImageComponent hideButton={hideButton} />
</div>
);
}
// ButtonComponent
function ButtonComponent() {
const [hideButton, setHideButton] = React.useState(false)
function handleClick() {
setHideButton(true)
}
return (
{
!hideButton && (
<li>
<img className="image"src="./icons/>
<Button onClick={handleClick} variant="outlined" className="button__rightpage" >Hide</Button>
<caption className="text"> Hide</caption>
</li >
)
}
)
}
// ImageComponent
const ImageComponent = ({ hideButton }) => {
return (
<div>
{
!hideButton && (
<div>
<img src='icons/icon.png' />
<caption>Image </caption>
</div>
)
}
</div>
)
}
you need to lift up the state to the most parent Component be accessible to the ButtonCommponent and the ImageComponent. in this Case App Component. however, if the ButtonComponent is rendered out of the hierarchy under the App Component tree, you should use the context API.
create a context and share the state on it and it will be accessible on the application level.
//#1. create context.
export const HiddenContext = React.createContext(false);
//#2. create the provider and share the state with it.
function HiddenProvider({ children }) {
const [hideButton, setHideButton] = React.useState(false);
function handleClick() {
setHideButton(true);
}
return (
<HiddenContext.Provider value={{ hideButton, handleClick }}>
{children}
</HiddenContext.Provider>
);
}
//#3. render the provider component to the most top parent component
export default function App() {
const { hideButton } = React.useContext(HiddenContext);
return (
<HiddenProvider>
<div className="App">
<ImageComponent hideButton={hideButton} />
<OtherComponentRenderTheButton />
</div>
</HiddenProvider>
);
}
// other component that render the button
function OtherComponentRenderTheButton() {
return <ButtonComponent />;
}
//ButtonComponent
function ButtonComponent() {
const { hideButton, handleClick } = React.useContext(HiddenContext);
return (
<React.Fragment>
{!hideButton && (
<li>
<img className="image" src="./icons" alt="" />
<Button
onClick={handleClick}
variant="outlined"
className="button__rightpage"
>
Hide
</Button>
<caption className="text"> Hide</caption>
</li>
)}
</React.Fragment>
);
}
// ImageComponent
const ImageComponent = () => {
const { hideButton } = React.useContext(HiddenContext);
return (
<div>
{!hideButton && (
<React.Fragment>
<img src="icons/icon.png" alt="" />
<caption>Image </caption>
</React.Fragment>
)}
</div>
);
};
working demo
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>
)
}
I'm trying to render an array containing some objects using the JS function map().
However when I return the text nothing is shown:
console.log(this.props.project.projects); // (2) [{…}, {…}]
this.props.project.projects.map((item, index) => {
console.log(item.projectDescription); //"Testproject"
return (
<div key={index}>
{item.projectDescription}
</div>
)
})
I just don't get it, why there is no text shown, since the console.log(item.projectDescription) shows exactly what I want to display.
Update:
It works when I change it to this:
return this.props.project.projects.map((item, index) => (
<div key={index} style={{ color: '#fff' }}>
{item.projektBeschreibung}
</div>
))
I already thought about using the foreach-method but I think it should actually work using the map()-function.
Here you can see also the render method of my Component.
class ProjectRow extends Component {
renderProjects() {
console.log(this.props.project);
if (this.props.project.loading) {
return (
<div style={{color: '#fff'}}>
Loading
</div>
)
} else {
console.log(this.props.project.projects);
this.props.project.projects.map((item, index) => {
console.log(item);
console.log(item.projektBeschreibung);
console.log(index);
return (
<div key={index}>
{item.projektBeschreibung}
</div>
)
})
}
}
render() {
return (
<div>
{this.renderProjects()}
</div>
);
}
}
The renderProjects function is not returning anything when it hits your else case. Here is an example of use:
renderProjects() {
console.log(this.props.project);
if (this.props.project.loading) {
return (
<div style={{color: '#fff'}}>
Loading
</div>
)
} else {
console.log(this.props.project.projects);
// added return statement here
return this.props.project.projects.map((item, index) => {
console.log(item);
console.log(item.projektBeschreibung);
console.log(index);
return (
<div key={index}>
{item.projektBeschreibung}
</div>
)
})
}
}
why not use map like below ?
render(){
return(
<div>
{this.props.project.projects.map((item, index) => (
<div key={index}>
{item.projectDescription}
</div>
))}
</div>
)
}