A beginner question: I am rendering a collection of items with React render function and I noticed that React automatically adds attribute is="null" to each rendered DOM element.
Why is React doing that? Is the key applied correctly or not? The respective code is:
export default class ItemList extends Component {
render() {
let { items } = this.props
items = items.map((item) => {
return <Item key={item.id} item={item} />
})
return (
<div>
{items}
</div>
)
}
}
export default class Item extends Component {
render() {
const { item } = this.props
return (
<div>
<h3>{item.attributes.name}</h3>
</div>
)
}
}
In the DOM, each div and h3 has attribute is="null"
<div is="null">
<h3 is="null">Item 1</h3>
</div>
This is Firefox issue and its already in pipeline
https://discuss.reactjs.org/t/is-null-attribute-on-every-tag/4032/7
https://github.com/facebook/react/pull/6896
Related
I wanna set the condition to my cart so that when the cart's length is greater than 0, it will be rendered the CartList and CartColumns component. However, it cannot be rendered. I don't know what is wrong! Please help me to fix my codes! Thank you so much!
Cart.js:
import CartList from "./cart/CartList"
const ProductContext = React.createContext();
export default class Cart extends React.Component {
render() {
return (
<div>
<ProductContext.Consumer>
{(value) => {
return <div>
if(cart.length>0){
return(
<div>
<CartColums/>
<CartList/>
</div>
)
}
</div>
}}
</ProductContext.Consumer>
</div>
);
}
}
Sandbox link for better observation:https://codesandbox.io/s/why-cant-i-fetch-data-from-a-passed-value-forked-buz0u?file=/src/cart/Cart.js
You simply had a syntax error (which got highlighted by the codesandbox link btw). Consider using an editor / IDE that highlights simple errors like this one.
EDIT: To elaborate a little further on the syntax error:
After {(value) => {return <div> you inserted js wihout wrapping it in another pair of braces. In this case the solution was to remove the return <div> here entirely as it was unecessary.
import CartList from "./cart/CartList"
const ProductContext = React.createContext();
export default class Cart extends React.Component {
render() {
return (
<div>
<ProductContext.Consumer>
{(value) => {
if(cart.length>0){
return(
<div>
<CartColums/>
<CartList/>
</div>
)
}
}}
</ProductContext.Consumer>
</div>
);
}
}
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;
I am new to React and am having trouble wrapping my head around props/states.
So I have component SortingVisualizer, that generates a visual representation of an unsorted array as follows:
class SortingVisualizer extends React.Component {
constructor(props){
super(props);
this.state = {
array: [],
};
}
componentDidMount(){
this.resetArray();
}
resetArray(){
const array = [];
for(let i = 0; i<100; i++){
array.push(this.getRandomInt(1,500))
}
this.setState({array});
}
//Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
render(){
const {array} = this.state;
return(
<div className="array-container">
{array.map((value, idx) => (
<div className = "array-elem" key = {idx} style = {{height: `${value}px`}}>
</div>
))}
</div>
);
}
}
export default SortingVisualizer;
Now, I have a Navbar Component with a button "Generate new Array" :
class Navbar extends React.Component {
render(){
return(
<nav className = "navbar">
<button className = "new-array-btn" onClick ={this.props.GNAclick}>Generate New Array</button>
</nav>
)
}
}
export default Navbar;
What I want to achieve is that on button click, resetArray will be called on SortingVisualizer so a new array will be generated.
Here is my App.js:
class App extends React.Component {
GNAclickHandler = () => {
console.log("clicked!");
/*
this.setState((prevState) => {
return {GNA: !prevState.GNA}
});
*/
}
render() {
return (
<>
<header>
<Navbar GNAclick = {this.GNAclickHandler}/>
</header>
<div className="App">
<SortingVisualizer />
</div>
</>
);
}
}
export default App;
I am not sure how to progress from here, any help will be appreciated.
Here is the website: https://roy-05.github.io/sort-visualizer/
I would recommend you to read this article https://reactjs.org/docs/thinking-in-react.html, so you can have a better understanding of state and props.
Regarding to your app, you just need to know which component has state and which will react to props change.
App.js
// Navbar manages the state
// and provides it to their children
// so they can rerender whenever the app state changes
class App extends React.Component {
state = {
data: Date.now()
}
createNewArray = () => {
this.setState({
data: Date.now()
})
}
render() {
return (
<React.Fragment>
<Navbar onGenerate={this.createNewArray}/>
<SortingVisualizer data={this.state.data}/>
</React.Fragment>
)
}
}
Navbar.js
// Navbar just notifies that
// and action has to be executed
// (the parent handles that action by creating a new set of data)
function Navbar(props) {
return (
<nav>
<button onClick={props.onGenerate}>Generate array</button>
</nav>
)
}
SortingVisualizer.js
// SortingVisualizer renders what App tells it to render
function SortingVisualizer(props) {
return (
<main>
{props.data}
</main>
);
}
I want to print out the list of numbers in my iteration, what am I doing wrong?
main.js component:
export class Main extends Component {
render() {
return (
<div>
<div className="wrapper">
<h1>car2go data</h1>
{Car2go.placemarks.map((content, index) => {
return <RenderTable content={content} key={index} />;
})}
</div>
</div>
);
}
}
export default Main;
renderTable.js component
import React, { Component } from "react";
export class RenderTable extends Component {
render() {
const { content } = this.props;
const { key } = this.props;
return (
<div>
<p>
<span>{key}</span>
{content.address}
</p>
</div>
);
}
}
export default RenderTable;
I'm able to print the content. address values, for each iteration, but I want to number the list, that's why I put a span before this. I can even see the key attribute printing out the number in inspector mode, but I need that in the HTML.
key in react has a special meaning and it is useful for listing and uniquely identifying the elements. Don't use key as a props unless it is used for unique listing. Ref
Try to replace this,
<RenderTable content={content} key={index} />
with this,
<RenderTable content={content} ind={index} />
and use like this,
export class RenderTable extends Component {
render() {
const { content } = this.props;
const { ind } = this.props;
return (
<div>
<p>
<span>{ind}</span>
{content.address}
</p>
</div>
);
}
}
You are trying to use the key prop in component. But that key prop is just for react's internal use. So, that key was not included in this.props by default. Hence, you need to use some other prop name (other than 'key') to pass index.
So, please change this from
<RenderTable content={content} key={index} />
to
<RenderTable content={content} key={index} num={index} />
then you can use num like
export class RenderTable extends Component {
render() {
const { content } = this.props;
const { num } = this.props;
return (
<div>
<p>
<span>{num}</span>
{content.address}
</p>
</div>
);
}
}
Hope this helps.
I created a custom Accordion component which again consist of two child components called AccordionTitle and AccordionContent:
The AccordionTitle component has a button. When clicked, the AccordionContent part toggles its style from display:none to block and back when clicked again.
AccordionTitle.js
class AccordionTitle extends Component {
constructor() {
super();
this.show = false;
}
toggle() {
this.show = !this.show;
if (this.props.onToggled) this.props.onToggled(this.show);
}
render() {
return (
<div style={this.props.style}>
<Button onClick={e => this.toggle(e)} />
{this.props.children}
</div>
);
}
}
export default AccordionTitle;
AccordionContent.js
class AccordionContent extends Component {
render() {
let style = this.props.style ? this.props.style : {};
style = JSON.parse(JSON.stringify(style));
style.display = this.props.show ? 'block' : 'none';
return (
<div style={style}>
{this.props.children}
</div>
);
}
}
export default AccordionContent;
Also, I use the following parent component:
Accordion.js
class Accordion extends Component {
render() {
return (
<div>
{this.props.children}
</div>
);
}
}
Accordion.Title = AccordionTitle;
Accordion.Content = AccordionContent;
export default Accordion;
Now, when I use the Accordion component, it's possible that I might need multiple accordions in a row which would look like this:
ProductAccordion.js
import React, { Component } from 'react';
import Accordion from '../Accordion/Accordion';
class ProductAccordion extends Component {
constructor() {
super();
this.state = {
show: false,
};
}
toggled() {
this.setState({
show: !this.state.show,
});
}
render() {
this.productsJsx = [];
const products = this.props.products;
for (let i = 0; i < products.length; i += 1) {
this.productsJsx.push(
<Accordion.Title onToggled={e => this.toggled(e, this)}>
{products[i].name}
<img src="{products[i].imgsrc}" />
</Accordion.Title>,
<Accordion.Content show={this.state.show}>
{products[i].name}<br />
{products[i].grossprice} {products[i].currency}<br />
<hr />
</Accordion.Content>,
);
}
return (
<Accordion style={styles.container}>
{this.productsJsx}
</Accordion>
);
}
}
export default ProductAccordion;
As you can see, I am grabbing the toggled Event from Accordion.Title and I bind it to the prop show of Accordion.Content via the toggled() method.
Now, this works perfectly fine as long as there is just one product, but if there are more of them, clicking on the button will toggle all AccordionContent instances.
How can I change this so that only the content-part which belongs to the title that contains the clicked button will be toggled?
I also have the feeling that the component Accordion should take care of this (rather than ProductAccordion) by allowing Accordion.Title to delegate the toggled event directly to its sibling Accordion.Content. How can I achieve this?
I would suggest storing the index of the open item in state, instead of a boolean. Then in your render, show={this.state.show} would be something like show={this.state.show === i}.
Full example:
import React, { Component } from 'react';
import Accordion from '../Accordion/Accordion';
class ProductAccordion extends Component {
constructor() {
super();
this.state = {
show: null,
};
}
toggled(event, ind) {
const index = this.state.index;
this.setState({ show:ind === index ? null : ind });
}
render() {
this.productsJsx = [];
const products = this.props.products;
for (let i = 0; i < products.length; i += 1) {
this.productsJsx.push(
<Accordion.Title onToggled={e => this.toggled(e, i)}>
{products[i].name}
<img src="{products[i].imgsrc}" />
</Accordion.Title>,
<Accordion.Content show={this.state.show === i}>
{products[i].name}<br />
{products[i].grossprice} {products[i].currency}<br />
<hr />
</Accordion.Content>,
);
}
return (
<Accordion style={styles.container}>
{this.productsJsx}
</Accordion>
);
}
}
export default ProductAccordion;
and this
class AccordionTitle extends Component {
constructor() {
super();
}
render() {
return (
<div style={this.props.style}>
<Button onClick={this.props.onToggled} />
{this.props.children}
</div>
);
}
}
export default AccordionTitle;