When an array of children are passed as prop and looped over to render in parent component, react is complaining about missing unique key identifier on array iteration.
What is the correct way to overcome the warning?
Can I pass key along with children under prop.
If I want to set key to children while rendering in parent, it is complaining child's key prop is read only.
Would be easier if you shared some code.
You shouldn't need to iterate the children to render then, unless you are augmenting them in some way.
class MyComponent extends React.Component {
render() {
return (
<div>
{this.props.children}
</div>
)
}
}
If you must, can you wrap them in a div and apply the key to that instead?
class MyComponent extends React.Component {
render() {
return (
<div>
{this.props.children.map((child, index) => (<div key={index}>{child}</div>))}
</div>
)
}
}
I haven't tried this but you might be able to clone the element and change the key
class MyComponent extends React.Component {
render() {
return (
<div>
{this.props.children.map((child, index) => {React.cloneElement(child, {key: index})})}
</div>
)
}
}
Related
I am new to react.js and I have stumbled upon a subject I can't wrap my head around. I have a long array of items which I want to display in two different rows, which is why I take chunks out of this array and add them to a nested array without key and values like so:
constructor(props) {
super(props)
this.state = {
characters: [["Anakin","Darth Vader","Snoke"], ["Darth Maul", "Yoda", "Luke Skywalker"]],
}
}
In the render function of the "Characters" component I use the map function and what I want is each array to be passed to the component "Subarray":
Characters component
render() {
return (
<div>
{this.state.characters.map((item, index )=> <Subarray key={index} title={item}></Subarray>)}
</div>
)
}
And then in the "Subarray" component I want to add a html element to every element in that array:
Subarray component
export class Subarray extends Component {
render() {
return (
<div>
{this.props.title}
</div>
)
}
}
I can't get each element of the array to be wrapped within a div element:
Output:
<div><div>AnakinDarth VaderSnoke</div><div>Darth MaulYodaLuke Skywalker</div></div>
You can do this, assuming this.props.title contains ["Anakin","Darth Vader","Snoke"] :
export class Subarray extends Component {
render() {
return (
<div>
{this.props.title.map((each, index) => <div key={index}>{each}</div>)}
</div>
)
}
}
I think that you have to change Subarray to be something like
export class Subarray extends Component {
render() {
return (
<div>
{this.props.title.map((item, index) => {
return <div key={index}>{item}</div>
})}
</div>
)
}
}
in this way you loop through each item in the subarray and render every one of them
I have a need to use forwarded refs
const InfoBox = React.forwardRef((props, ref) => (
<div ref={ref} >
<Rings >
</Rings>
<Tagline />
</div>
));
I also happen already have the code written like this
class InfoBox extends React.Component {
constructor(props) {
super(props)
}
render () {
return (
<div >
<Rings />
<Tagline />
</div>
)
}
basically my InfoBox needs to be a Component because it holds some state, but I also want it to behave like an object that can receive refs from the parent and forward them down to the children (basically React.forwardRef)
After familiarizing myself with React.forwardRef, I can't figure out how to get it to work with my existing React components, which already have functionality attached to state.
do I need to separate the two objects, and wrap one within the other or is there a way I can achieve this in the same object?
the code that wraps Infobox looks like
class AppContainer extends React.Component {
constructor(props) {
super()
this.infobox_ref = React.createRef()
}
componentDidMount() {
// this.infobox_ref.current.innerHTML should return the inner HTML of the infobox
}
render() {
return (
<InfoBox ref={this.infobox_ref}>
)
}
am I using forwarded refs correctly?
In React, the ref prop is not forwarded by default. In order to get a reference in a child component, you have 2 options:
Using a function component wrapped in the forwardRef function. You have already done this:
const InfoBox = React.forwardRef((props, ref) => (
<div ref={ref} >
<Rings >
</Rings>
<Tagline />
</div>
));
Changing the name of the ref prop.
// Parent Component
class AppContainer extends React.Component {
constructor(props) {
super()
this.infobox_ref = React.createRef()
}
componentDidMount() {
// this.infobox_ref.current.innerHTML should return the inner HTML of the infobox
}
render() {
return (
<InfoBox infoboxRef={this.infobox_ref}>
)
}
}
// Child Component
class InfoBox extends React.Component {
render () {
return (
<div ref={this.props.infoboxRef}>
<Rings />
<Tagline />
</div>
)
}
}
Of course, you can also combine them, allowing you to still pass to the ref prop from the parent, but consuming the "fixed" prop in the child class component, as shown here by #tubu13:
class InfoBox extend React.Component{
render() {
return (
<div ref={this.props.infoboxRef}>
<Rings />
<Tagline />
</div>
)
}
}
export default React.forwardRef((props, ref) => <InfoBox {...props} infoboxRef={ref} />)
How to create a ref for each instance of a component
I've extracted some code into it's own component. The component is a PlayWhenVisible animation component that plays/stops the animation depending on whether the element is in view.
I'm creating a ref inside the component constructor but since I'm getting some lag when using 2 instances of the component I'm wondering if I should create the refs outside the component and pass them in as props or whether there's a way to create a new instance for each compoenent instance.
import VisibilitySensor from "react-visibility-sensor";
class PlayWhenVisible extends React.Component {
constructor(props) {
super(props);
this.animation = React.createRef();
this.anim = null;
}
render() {
return (
<VisibilitySensor
scrollCheck
scrollThrottle={100}
intervalDelay={8000}
containment={this.props.containment}
onChange={this.onChange}
minTopValue={this.props.minTopValue}
partialVisibility={this.props.partialVisibility}
offset={this.props.offset}
>
{({ isVisible }) => {
isVisible ? this.anim.play() : this.anim && this.anim.stop();
return (
// <div style={style}>
<i ref={this.animation} id="animation" className={this.props.class} />
);
}}
</VisibilitySensor>
);
}
}
The issue was caused by the VisibilityChecker component which was overflowing the container and causing it to be erratic when firing.
I want to render BlackSpark when RedSpark is clicked, but I'm not sure how to change the state of a component in another component. I know how to set state in the component itself, but how do I affect another component when I click a different component?
class BlackSpark extends React.Component {
render() {
return (
<div className="black"></div>
);
}
}
class RedSpark extends React.Component {
render() {
return (
<div className="red"></div>
);
}
}
class App extends React.Component {
render() {
return (
<div>
<BlackSpark />
<RedSpark />
</div>
);
}
}
In React, there's a concept of component composition as you've already embraced -- it allows you to accomplish what you want by rendering children based on the parent's state, another key concept known as lifting state up. What this means, is if you have mutually dependent components, create a single parent which composes them, and have state in the parent control the presentation and logic of the children. With the parent App, you can keep your state inside App, and based on App's state, conditionally render whatever you want -- either BlackSpark or both. For example, using the logical && operator:
{condition && <Component />}
This will only render <Component> when condition is truthy, or else it will not render anything at all (except for when condition is 0). Applying it to this situation, try adding state to your App component to utilize conditional rendering.
There's another key concept you need to understand: component props. They are essentially inputs to a component, certain properties passed to the component to tell how it should behave -- like attributes on regular HTML elements such as input placeholders, URLs, and event handlers. For example:
<Component foo="bar" bar={3} />
This will pass the props foo and bar down to Component with the values "bar" and 3 respectively and are accessible through this.props. If you were to access this.props.foo inside the Component component it would give you "bar". If you pair this up with composition, you can accomplish what you want:
class Example extends React.Component {
constructor() {
super();
this.state = {
showHello: true
}
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.setState(prevState => ({
showHello: !prevState.showHello
}));
}
render() {
return (
<div>
{this.state.showHello && <Child2 />}
This is a test.
<Child1 onClick={this.handleChange} />
</div>
);
}
}
class Child1 extends React.Component {
render() {
return <div onClick={this.props.onClick}>Click me!</div>
}
}
class Child2 extends React.Component {
render() {
return <div>Hello!</div>
}
}
ReactDOM.render(<Example />, 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>
The above example lifts state up by having a parent compose the children and maintain the state. It then uses props to pass down an onClick handler to Child1, so that whenever Child1 is clicked, the state of the parent changes. Once the state of the parent changes, it will use conditional rendering to render <Child2> if the condition is truthy. Further reading at the React documentation and on the logical && operator.
I know how to set state in the component itself, but how do I affect another component when I click a different component?
The recommended way to do it would be to create a parent component that has the state. You'd then use that state to determine when to render the other child component.
I want to render BlackSpark when RedSpark is clicked, but I'm not sure how to change the state of a component in another component. Also, what if I want to hide BlackSpark when GreenSpark is clicked and GreenSpark is inside BlackSpark?
In this case, here's how you'd do it.
const GreenSpark = ({ onClick }) => (
<button className="green" onClick={onClick}>X</button>
)
const BlackSpark = ({ onClick }) => (
<div className="black">
<GreenSpark onClick={onClick} />
</div>
)
const RedSpark = ({ onClick }) => (
<div className="red" onClick={onClick}></div>
)
class Spark extends React.Component {
constructor(props) {
super(props)
this.state = {
showBlack: false
}
this.boundShowBlack = this.showBlack.bind(this)
this.boundHideBlack = this.hideBlack.bind(this)
}
showBlack() {
this.setState({ showBlack: true })
}
hideBlack() {
this.setState({ showBlack: false })
}
render() {
return (
<div>
<RedSpark onClick={this.boundShowBlack} />
{this.state.showBlack && <BlackSpark onClick={this.boundHideBlack} />}
</div>
)
}
}
I have checked this link
So far, I'm not able to understand the handler part. So I was hoping for a more simple example perhaps?
Here is my main parent component:
class appTemplate extends React.Component {
render() {
return (
<div>
<Header lang={this.props.route.path}/>
{this.props.children}
<Footer lang={this.props.route.path}/>
</div>
);
}
}
What I want to do is pass down the prop this.props.route.path to my child components which is this.props.children.
I'm not really fully familiar with all the terms even though I've been touching React already for the last few months.
An example with a proper explanation would be greatly appreciated. Thanks.
The proper way to achieve that is using React.Children.map()
class appTemplate extends React.Component {
render() {
return (
<div>
<Header lang={this.props.route.path}/>
{React.Children.map(
this.props.children,
child => React.cloneElement(child,
{
customProp: "Here is your prop",
path: this.props.route.path
})
)}
<Footer lang={this.props.route.path}/>
</div>
);
}
}
React has a cloneElement function. The idea is to clone the children object, passing on path as a part of the props:
class appTemplate extends React.Component {
render() {
let children = null;
if (this.props.children) {
children = React.cloneElement(this.props.children, {
path: this.props.route.path
})
}
return (
<div>
<Header lang={this.props.route.path}/>
{children}
<Footer lang={this.props.route.path}/>
</div>
);
}
}
You should then be able to access the path using this.props.path within a child element, but (from what I remember) not from within elements nested within the child.
You can read more about cloning and passing values here:
https://facebook.github.io/react/docs/top-level-api.html#react.cloneelement