I'm creating a component where I'd like to update the className prop of this.props.children.
To accomplish this I'm using the React.Children utilities. React.Children.map(children) to create a new array from the children and React.cloneElement(child) to manipulate the child props.
The map and cloneElement work in some ways. I can add props and change the children of the individual elements. But props.className is not propagated to the class of the child when rendered. That is, I see the update to props.className in the new children array, but the rendered children don't contain the new props.className.
Is this the incorrect way to update props.className for this.props.children? If so, what is the correct method?
class List extends React.Component {
constructor(props) {
super(props);
this.items = React.Children.map(this.props.children, item => {
const className = `${item.props.className} test`;
const props = { ...item.props, className: className };
return React.cloneElement(item, props, "TEST");
});
}
render() {
console.log(this.items);
return (
<ul>
{this.items}
</ul>
);
}
}
class ListItem extends React.Component {
render() {
return (
<li className="item">
{this.props.children}
</li>
);
}
}
class Application extends React.Component {
render() {
return (
<List>
<ListItem ref="item1" className="item">A</ListItem>
<ListItem ref="item2" className="item">B</ListItem>
<ListItem ref="item3" className="item">C</ListItem>
<ListItem ref="item4" className="item">D</ListItem>
</List>
);
}
}
/*
* Render the above component into the div#app
*/
ReactDOM.render(<Application />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.0/react-dom.min.js"></script>
<div id="app"></app>
It's propagated... You're just not using it.
convert
<li className="item">
to
<li className={this.props.className}>
class List extends React.Component {
constructor(props) {
super(props);
this.items = React.Children.map(this.props.children, item => {
const className = `${item.props.className} test`;
const props = { ...item.props, className: className };
return React.cloneElement(item, props, "TEST");
});
}
render() {
return (
<ul>
{this.items}
</ul>
);
}
}
class ListItem extends React.Component {
render() {
return (
<li className={this.props.className}>
{this.props.children}
</li>
);
}
}
class Application extends React.Component {
render() {
return (
<List>
<ListItem ref="item1" className="item">A</ListItem>
<ListItem ref="item2" className="item">B</ListItem>
<ListItem ref="item3" className="item">C</ListItem>
<ListItem ref="item4" className="item">D</ListItem>
</List>
);
}
}
/*
* Render the above component into the div#app
*/
ReactDOM.render(<Application />, document.getElementById("app"));
.test{
background-color: red
}
.test {
color: orange;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.0/react-dom.min.js"></script>
<div id="app"></app>
Related
I'm trying to pass a callback function from parent->child, but when the child component is rendered I get the error: TypeError: this.props.setCurrentWindow is not a function.
Parent component where I am trying to pass the function setCurrentWindow
class Parent extends Component {
constructor(props){
super(props);
this.setCurrentWindow = this.setCurrentWindow.bind(this);
}
setCurrentWindow(){
console.log("called")
}
render(){
return(
<Child
setCurrentWindow={this.setCurrentWindow}
/>)}
}
child component where I am trying to call setCurrentWindow
class Child extends Component{
constructor(props){
super(props)
}
render(){
return(
<div
onClick={()=>{this.props.setCurrentWindow()}}>
{this.props.children}
</div>
)}
}
Why is setCurrentWindow not being recognized as a function here?
Please check this example where I only found the difference is to have child element like <div><h1>Hello</h1></div> that was not in your code. other than this everything is working fine. When I click on the div, it writes called in console
export default class Parent extends Component {
constructor(props) {
super(props);
this.setCurrentWindow = this.setCurrentWindow.bind(this);
}
setCurrentWindow() {
console.log("called")
}
render() {
return (
<Child
setCurrentWindow={this.setCurrentWindow}
>
<div>
<h1>Hello</h1>
</div>
</Child>
)
}
}
class Child extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div onClick={() => {
this.props.setCurrentWindow()
}}>
{this.props.children}
</div>
);
}
}
Try this:
parent.jsx:
class Parent extends Component {
// code omitted for brevity
handleSetWindow = () => {
//
};
render() {
return (
<div>
<Child onSetWindow={this.handleSetWindow}
/>
</div>
);
}
}
child.jsx:
class Child extends Component {
render() {
return (
<div>
<button onClick={() => this.props.onSetWindow()} >
Set
</button>
</div>
);
}
}
A stupid answer but the final solution here is that not all instances of my child components were being passed this.setCurrentWindow hence the undefined error. Durrr! Thanks for the responses!
I am trying to iterate over an array and assign fields to corresponding child components.
The way I am currently doing it looks like this:
class CardExtension extends React.Component {
render() {
return (
<div>
{ this.props.value }
</div>
);
}
}
class Card extends React.Component {
render() {
return (
<div>
{ this.props.title }
</div>
);
}
}
Once child components are defined and imported, I do push new instances of these classes to a completely new array:
class CardContainer extends React.Component {
render() {
var arr = [
{
'id':1,
'title':'title',
'value':'test_value'
}
]
var elements=[];
for(var i=0;i<arr.length;i++){
elements.push(<Card title={ arr[i].value } />);
elements.push(<CardExtension value={ arr[i].title } />);
}
return (
<div>
{elements}
</div>
);
}
}
Is there any way to accomplish the same using the following format
class CardContainer extends React.Component {
render() {
var arr = [
{
'id':1,
'title':'title',
'value':'test_value'
}
]
return (
<div>
{arr.map((el, idx) => (
<Card title={ el.value } />
<CardExtension value={ el.title } />
))}
</div>
);
}
}
#update
The problem is that whenever I do use the latest solution, I do receive following error message: Adjacent JSX elements must be wrapped in an enclosing tag (39:24)
Working solution:
import React, { Component, Fragment } from 'react';
export default class CardContainer extends React.Component {
render() {
var arr = [
{
'id':1,
'title':'title',
'value':'test_value'
}
]
return (
<div>
{arr.map((el, idx) => (
<Fragment>
<Card title={ el.value } />
<CardExtension value={ el.title } />
</Fragment>
))}
</div>
);
}
}
The error message "Adjacent JSX elements must be wrapped in an enclosing tag" means just that: the <Card> and <CardExtension> elements need to be wrapped in a parent tag.
Now, you probably don't want to wrap the elements in a <div> (since it would create unnecessary DOM nodes), but React has a nifty thing called "Fragments", letting you group elements without extra nodes.
Here is a working solution for your example, using the fragment short syntax:
class CardContainer extends React.Component {
render() {
var arr = [{
'id':1,
'title':'title',
'value':'test_value'
}]
return (
<div>
{arr.map((el, idx) => (
<>
<Card title={ el.value } />
<CardExtension value={ el.title } />
</>
))}
</div>
);
}
}
I'm new to ReactJS and I would like to communicate between my components.
When I click an image in my "ChildA" I want to update the correct item image in my "ChildB" (type attribute in ChildA can only be "itemone", "itemtwo", "itemthree"
Here is what it looks like
Parent.js
export default class Parent extends Component {
render() {
return (
<div className="mainapp" id="app">
<ChildA/>
<ChildB/>
</div>
);
}
}
if (document.getElementById('page')) {
ReactDOM.render(<Builder />, document.getElementById('page'));
}
ChildA.js
render() {
return _.map(this.state.eq, ecu => {
return (
<img src="../images/misc/ec.png" type={ecu.type_eq} onClick={() => this.changeImage(ecu.img)}/>
);
});
}
ChildB.js
export default class CharacterForm extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{ name: "itemone" image: "defaultone.png"},
{ name: "itemtwo" image: "defaulttwo.png"},
{ name: "itemthree" image: "defaultthree.png"},
]
};
}
render() {
return (
<div className="items-column">
{this.state.items.map(item => (<FrameCharacter key={item.name} item={item} />))}
</div>
);
}
}
I can retrieve the image on my onClick handler in my ChildA but I don't know how to give it to my ChildB. Any hints are welcomed, thanks you!
What you need is for Parent to pass an event handler down to ChildA which ChildA will call when one of the images is clicked. The event handler will call setState in Parent to update its state with the given value, and then Parent will pass the value down to ChildB in its render method.
You can see this working in the below example. Since I don't have any actual images to work with—and to keep it simple—I've used <button>s instead, but the principle is the same.
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
clickedItem: 'none',
};
}
render() {
return (
<div>
<ChildA onClick={this.handleChildClick}/>
<ChildB clickedItem={this.state.clickedItem}/>
</div>
);
}
handleChildClick = clickedItem => {
this.setState({ clickedItem });
}
}
const items = ['item1', 'item2', 'item3'];
const ChildA = ({ onClick }) => (
<div>
{items.map(name => (
<button key={name} type="button" onClick={() => onClick(name)}>
{name}
</button>
))}
</div>
);
const ChildB = ({clickedItem}) => (
<p>Clicked item: {clickedItem}</p>
);
ReactDOM.render(<Parent/>, document.querySelector('div'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.development.js"></script>
<div></div>
I have a React component MoviesGallery.js with the following configuration:
class MoviesGallery extends Component {
constructor(props) {
super(props)
this.state = { currentImage: 0 };
this.closeLightbox = this.closeLightbox.bind(this);
this.openLightbox = this.openLightbox.bind(this);
this.gotoNext = this.gotoNext.bind(this);
this.gotoPrevious = this.gotoPrevious.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({movies_genre: nextProps.movies_genre})
}
I have rendered the component in my main App.js file like so:
class App extends Component {
render() {
return (
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
<RaisedButton primary={true} label="Query" className="header_buttons"/>
<RaisedButton secondary={true} label="Reset" className="header_buttons"/>
</header>
<MoviesGallery/>
</div>
</MuiThemeProvider>
);
}
}
I want to update the props of my MoviesGallery component without recreating the component. Since I already added the componentWillReceiveProps() to MoviesGallery component, how can I make it so when 'Query' button is clicked, it will pass new props to the already rendered MoviesGallery and componentWillReceiveProps() should cause it to re-render since the state will change.
Just confused about the function that will change the props themselves on-click of the rendered MoviesGallery component.
Thanks in advance!
When a parent pass a new (value) prop to the child, the child component will call the render method automatically. There is no need to set a local state inside the child component to "store" the new prop.
Here is a small example of a Counter that receives a count prop and just displays it, while the parent App in this case will change the value in its state and pass the new value to Counter:
class Counter extends React.Component {
render() {
const { count } = this.props;
return (
<div>{count}</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
onClick = () => {
this.setState({ count: this.state.count + 1 });
}
render() {
const { count } = this.state;
return (
<div>
<Counter count={count} />
<button onClick={this.onClick}>Add to counter</button>
</div>);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
you can use the 'state' for your MovieGallery.js props because the state is an object that changes and you must your code like below :
class App extends Component {
state = {
query : null
}
myFunction(query){
this.setState({query});
}
render() {
return (
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
<RaisedButton primary={true} label="Query" className="header_buttons" onClick={this.myFunction = this.myfunction.bind(this)}/>
<RaisedButton secondary={true} label="Reset" className="header_buttons"/>
</header>
<MoviesGallery newProps = {this.state.query}/>
</div>
</MuiThemeProvider>
);
}
}
i hope it helps
In my React component, I'm displaying a list of items -- each in its own DIV element with a unique id i.e. <div id="abc-123">.
I'm also using react-perfect-scrollbar to make the whole thing nicer looking.
I keep a variable in my reducer named activeElementId and when the value of activeElementId changes, I want to automatically scroll to that item on the screen.
Setting the activeElementId is the easy part but I'm not sure how to scroll to that element and would appreciate some pointers.
This is the parent component that contains the ListComponent.
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
return(
<div>
{this.props.items.length > 0
?
<PerfectScrollBar>
<ListComponent items={this.props.items} />
</PerfectScrollBar>
: null}
</div>
);
}
}
My ListComponent is a presentational component:
const ListComponent = ({ items }) => {
return(
<ul className="pretty-list">
{items.map(item => <ItemComponents item={item} />)}
</ul>
);
}
export default ListComponent;
And the ItemComponent is a presentational component as well:
const ItemComponent = ({ Item }) => {
return(
<li>
<div id={item.id}>
{item.someProperty}
</div>
</li>
);
}
export default ItemComponent;
I really like the idea of keeping ListComponent and ItemComponent separate and as presentational components as that helps keep the code simpler and easier to manage. Not sure if that would make it difficult to implement the auto scroll logic though.
The library you use has a method called setScrollTop. You can use it with getBoundingClientRect. To use getBoundingClientRect you need to have the dom-element. You didn't give any code about how you are setting or getting the active element but I'll try to give you an example. Maybe it will help you to implement on your code.
Example
class MyComponent extends Component {
constructor(props) {
super(props);
}
_onListItemChange = (itemsPosition) => {
this.scrollbar.setScrollTop(itemsPosition.top);
}
render() {
return (
<div>
{this.props.items.length > 0 ?
<PerfectScrollBar ref={(scrollbar) => { this.scrollbar = scrollbar; }}>
<ListComponent
items={this.props.items}
onListItemChange={this._onListItemChange} />
</PerfectScrollBar>
: null}
</div>
);
}
const ListComponent = ({ items, onListItemChange }) => {
return(
<ul className="pretty-list">
{items.map(item => (
<ItemComponents
item={item}
onListItemClick={onListItemChange} />
))}
</ul>
);
}
export default ListComponent;
import { render, findDOMNode } from 'react-dom';
class ListItem extends React.Component {
_onClick = () => {
let domElement = findDOMNode(this.item);
this.props.onListItemClick(domElement.getBoundingClientRect());
}
render() {
const { item } = this.props;
return(
<li>
<div id={item.id} ref={(item) => { this.item = item; }} onClick={this._onClick}>
{item.someProperty}
</div>
</li>
);
}
}