I'm really new to web programming.
Is this function React, JSX, or others?
How do I get console log from this function? For example, printing out children and/or router values to console log.
I'd like to hide 'prev' when current page is a specific page. How can this be done?
Thanks in advance.
const App = ({children, router}) => (
<div className="App">
<Stepper/>
<div className='App-main'>
<Row gutter={40}>
<Col span={20}>
{ children }
</Col>
<Col span={4}>
<Navigator
label={label(router.location.pathname)}
action={action(router, router.location.pathname)}
prev={prev(router.location.pathname)}
/>
</Col>
</Row>
</div>
</div>
)
As Zfrisch mentioned, it's a mix. App is a functional component (React), the stuff returned in the function is XML.
The rounded brackets are an implicit return. If you change it to curly braces, you can do stuff then return ( xmlGoeshere ); Example:
const App = ({children, router}) => {
console.log("hello world");
return (
<div className="App">
etc...
);
};
It's hard to answer this in a way that works for you without more info. You can either use a ternary to make prev falsy, you can change the prev function, or modify the Navigator's render depending on what you want to achieve.
Related
I am using card from bootstrap react but it is not rendering anything on the screen, below is my code. Currently it is displaying it but not as intended, I have tried modifying it but it stops working.
I appreciate any help.
function Products(props) {
let params = useParams()
let [ product, setProduct ] = useState()
function loading() {
return <div className="w-25 text-center"><Spinner animation="border" /></div>
}
function Products(products) {
if (products === null) return;
return products.map((product) => (
<ListGroup.Item key={product.Id}>
<Link to={`/products/${product.name}`} key={product.id}> {product.name}
</Link>
</ListGroup.Item>
));
}
let { id, name, description, price, origin, imgUrl } = product;
return (
<>
<h1>Products</h1>
<Card className="align-self-start w-25">
<Card.Body>
<Card.Title>{origin}</Card.Title>
<Card.Text>
<strong>Description:</strong> <span>{description}</span>
<br/>
<ProductContext.Consumer>
{({ products }) => Products(products)}
</ProductContext.Consumer>
</Card.Text>
</Card.Body>
</Card>
</>
);
if (product === undefined) return loading()
return product.id !== parseInt(params.productId) ? loading() : Products()
}
export default Products;
I feel like the logic in your code isn't sound. First of all, useState's product doesn't seem to be used OR set (at least in this code snippet).
The products is coming from the ProductContext.Consumer, which we don't know the code for.
A couple things about your code to fix/look into:
use const with useState
You aren't using your useState getter or setter in this code snippet.
Make sure no locally declared names conflict with imports or other declarations(either rename imports like import BootstrapComponent as BSComponent from "bootstrap" or pick a unique name for your component). You have two Products nested. Whether the scope is sound, name them more purposefully like Products and ProductsWrapper or something.
as Xavier said, you have unreachable code
My guess, is either you have naming conflicts or the Consumer isn't working as expected. Gotta fix your logic and perhaps move the inner Products function out to simplify things for you.
I'm not sure if this is related to your problem, but it seems that if the product is undefined, you do not display the loading() part. This code is unreachable because it is after the return statement of your component function.
function Products(props) {
let params = useParams();
let [product, setProduct] = useState();
function loading() {
return (
<div className='w-25 text-center'>
<Spinner animation='border' />
</div>
);
}
if (product === undefined) return loading();
let { id, name, description, price, origin, imgUrl } = product;
return (
<>
<h1>Products</h1>
<Card className='align-self-start w-25'>
<Card.Body>
<Card.Title>{origin}</Card.Title>
<Card.Text>
<strong>Description:</strong> <span>{description}</span>
</Card.Text>
</Card.Body>
</Card>
</>
);
}
export default Products;
Also, it seems that you have name that might conflicts: Try to avoid having a function Products() inside a function that is already called Products. My recommendation for this scenario is to create 2 different components and split their code into 2 different files ;-)
I am using Material-UI within my ReactJS app to create a table that, when clicked, expands to show more detailed info (a new row just beneath the clicked row). As example, here is a minimal toy example:
https://codesandbox.io/s/material-collapse-table-forked-t6thz
The code relevant to the problem is:
<Collapse
in={open}
timeout="auto"
TransitionProps={{
mountOnEnter: true,
unmountOnExit: true,
}}
mountOnEnter
unmountOnExit
>
<div>
{/* actual function calls here; produces JSX output */}
{console.log("This should not execute before expanding!")}
Hello
</div>
</Collapse>;
Do note that the console.log() statement is just a simple replacement for my actualy functionality, which involves some API calls that are made when a row is clicked, and the corresponding info is displayed. So instead of console.log() I would actually call some other function.
I find that the console.log() statement executed on initial page render itself, even though in=false initially. How can I prevent this? Such that the function calls take place only when the Collapse is expanded. I initially thought this would be automatically handled by using mountOnEnter and unmountOnExit, but that does not seem to be the case. Any help would be appreciated, that could fix this problem in the sample example above.
I am working on an existing open source project, and therefore do not have the flexibility to restructure the existing codebase a lot. I would ideally have loved to implement this differently, but don't have that option. So posting here to know what options I might have given the above scenario. Thanks.
Problem
The children are rendered on initial load because they're defined within the Row component.
Solution
Move the Collapse children to its own React component. This won't render the children until the Collapse is opened. However, it'll re-render the child component when Collapse is closed. So depending on how you're making the API call and how other state interacts with this component, you may want to pass open to this component and use it as an useEffect dependency.
For example:
const Example = ({ open }) => {
React.useEffect(() => {
const fetchData = async () => {...};
if(open) fetchData();
}, [open]);
return (...);
}
Demo
Code
A separate React component:
const Example = ({ todoId }) => {
const [state, setState] = React.useState({
error: "",
data: {},
isLoading: true
});
const { data, error, isLoading } = state;
React.useEffect(() => {
const fetchData = async () => {
try {
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId}`
);
if (res.status !== 200) throw String("Unable to locate todo item");
const data = await res.json();
setState({ error: "", data, isLoading: false });
} catch (error) {
setState({ error: error.toString(), data: {}, isLoading: false });
}
};
fetchData();
/* eslint-disable react-hooks/exhaustive-deps */
}, []);
return (
<div
style={{
textAlign: "center",
color: "white",
backgroundColor: "#43A047"
}}
>
{error ? (
<p style={{ color: "red" }}>{error}</p>
) : isLoading ? (
<p>Loading...</p>
) : (
<>
<div>
<strong>Id</strong>: {data.id}
</div>
<div>
<strong>Title</strong>: {data.title}
</div>
<div>
<strong>Completed</strong>: {data.completed.toString()}
</div>
</>
)}
</div>
);
};
The Example component being used as children to Collapse (also see supported Collapse props):
<Collapse
in={open}
timeout="auto"
// mountOnEnter <=== not a supported prop
// unmountOnExit <=== not a supported prop
>
<Example todoId={todoId + 1} />
</Collapse>
Other Thoughts
If the API data is static and/or doesn't change too often, I'd recommend using data as a dependency to useEffect (similar to the open example above). This will limit the need to constantly query the API for the same data every time the same row is expanded/collapsed.
Firstly, huge thanks to Matt for his detailed explanation. I worked through his example, and expanded on it to work for me as required. The main takeaway for me was: "Move the Collapse children to its own React component."
The solution posted by Matt above, I felt, didn't completely solve the problem for me. E.g. if I add a console.log() statement to the render() of the new child component (<Example>), I still see it being executed before it is mounted.
Adding mountOnEnter and unmountOnExit solved this problem:
But as Matt mentioned, the number of times the children were getting rendered was still a problem. So I slightly changed some bits (also simplified the code a bit):
Essentially, I do this now:
My child component is:
function Example(props) {
return (
<div
style={{
fontSize: 100,
textAlign: "center",
color: "white",
backgroundColor: "#43A047"
}}
>
{props.flag && console.log("This should not execute before expanding!")}
{props.value}
</div>
);
}
and I call it from the parent component as:
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<Collapse in={open} timeout="auto" mountOnEnter unmountOnExit>
<Example value={row.name} flag={open} />
</Collapse>
</TableCell>
</TableRow>
Note that the parameter flag is essential to avoid the function execution during closing of the <Collapse>.
<Experience
startYear={2019}
endYear={2019}
jobName="First Job"
jobDescription="1. Providing API calls support for Headless CMS.2. Provide escalated ticket/incident management support"
/>
Here, I wanted to break line after 2nd point, I tried '\n' and br, this Tag is Made in JSX by following code:
import React, { Component } from 'react';
import { Grid, Cell } from 'react-mdl';
class Experience extends Component {
render() {
return(
<Grid>
<Cell col={4}>
<p>{this.props.startYear} - {this.props.endYear}</p>
</Cell>
<Cell col={8}>
<h4 style={{marginTop:'0px'}}>{this.props.jobName}</h4>
<p>{this.props.jobDescription}</p>
</Cell>
</Grid>
)
}
}
export default Experience;
I'd go by passing a jsx component as a prop to the child, instead of just raw text. This gives you more flexibility and control of how the UI will be rendered/ordered, within the parent component.
const Parent = () => (
<Child
jobDescription={(
<div style={{ flexDirection: 'column' }}>
<div>1. Providing API calls support for Headless CMS.</div>
<div>2. Provide escalated ticket/incident management support.</div>
</div>
)}
/>
)
const Child = (props) => (
<div>
<div>Child specific stuff</div>
{props.jobDescription}
</div>
)
Use an array, then loop the array to output for each item. Using string wont work because it is escaped when goes into props. Although Im aware that it might not be suitable for job description field.
Or, use you can write html tag in the string then render using https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml at your own risk.
I'm using a thing called react-firebase-js to handle firebase auth, but my understanding of react and of the provider-consumer idea is limited.
I started with a built a very big JSX thing all at the top level, and that works without warnings. But when I try to break it into components, I got the warning shown in the title and a few others.
This works without warning...
// in App.js component
render() {
return (
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<FirebaseAuthConsumer>
{({ isSignedIn, user, providerId }) => {
if (isSignedIn) {
return (
// ui for signed in user
);
} else {
if (this.state.confirmationResult) {
return (
// ui to get a phone number sign in
);
} else {
return (
// ui to verify sms code that was sent
);
}
}
}}
</FirebaseAuthConsumer>
</header>
);
}
But this, better design, I thought, generates errors/warnings...
// in App.js component
render() {
return (
<MuiThemeProvider>
<FirebaseAuthProvider {...config} firebase={firebase}>
<div className="App">
<IfFirebaseAuthed>
<p>You're authed buddy</p>
<RaisedButton label="Sign Out" onClick={this.signOutClick} />
</IfFirebaseAuthed>
<IfFirebaseUnAuthed>
<Authenticater /> // <-- this is the new component
</IfFirebaseUnAuthed>
</div>
</FirebaseAuthProvider>
</MuiThemeProvider>
);
}
// in my brand new Authenticator component...
render() {
return (
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<FirebaseAuthConsumer>
{({ isSignedIn, user, providerId }) => {
if (isSignedIn) {
return (
<div>
<pre style={{ height: 300, overflow: "auto" }}>
{JSON.stringify({ isSignedIn, user, providerId }, null, 2)}
</pre>
</div>
);
} else {
if (this.state.confirmationResult) {
return (
// ui to get a phone number sign in
);
} else {
return (
// ui to verify an sms code that was sent
);
}
}
}}
</FirebaseAuthConsumer>
</header>
);
}
The errors/warnings look like this...
[Error] Warning: React does not recognize the isSignedIn prop on a
DOM element. If you intentionally want it to appear in the DOM as a
custom attribute, spell it as lowercase issignedin instead. If you
accidentally passed it from a parent component, remove it from the DOM
element.
[Error] Warning: React does not recognize the providerId prop on a
DOM element. If you intentionally want it to appear in the DOM as a
custom attribute, spell it as lowercase providerid instead. If you
accidentally passed it from a parent component, remove it from the DOM
element.
[Error] Error: Unable to load external reCAPTCHA dependencies!
(anonymous function) (0.chunk.js:1216) [Error] Error: The error you
provided does not contain a stack trace.
Am I misunderstanding how to use provider-consumers, or is there an error in the react-firebase code, or am I doing some other thing wrong? Thanks.
Presumably, this line must be the culprit:
<FirebaseAuthProvider {...config} firebase={firebase}>
Your config object currently holds fields isSignedIn and providerId, and you must be sending those down to children components, and ultimately to a DOM element. Try removing those fields from the object before you send them down:
const { providerId, isSignedIn, ...authProviderConfig } = config
That way, your object authProviderConfig will not hold the providerId or isSignedIn attributes.
Even better, you can rebuild the configuration object explicitly to avoid any further confusion:
const authProviderConfig = { /* The fields from config FirebaseAuthProvider actually needs */ }
You should also check your FirebaseAuthProvider component to see how it's using those props, and avoid spreading them down to DOM elements.
Related documentation: https://reactjs.org/warnings/unknown-prop.html
This warning appears because you passed a prop on a component that it is not valid.
For example, this
<Component someUnknowprop='random-text' />
will trigger the warning. In order to get rid of the warning you should find out where that warning is coming from. The stack trace should give you a hint.
Adding $ to the prop name fixed it for me.
.tsx file:
<Wrapper $offset={isOffset}>
And on the .style.tsx file:
height: ${({ $offset }) => ($offset ? 'calc(100% + 20px)' : '100%')};
In my case, I was getting this error when using the IfFirebaseAuthed component from react-firebase.
You must make sure that you return a function inside of this component.
I changed this:
<IfFirebaseAuthed>
... My authenticated code here ...
</IfFirebaseAuthed>
To this:
<IfFirebaseAuthed>
{() => (
... My authenticated code here ...
)}
</IfFirebaseAuthed>
And this issue went away.
Check your custom props
In my case, I created a reusable hide component. (Initially, it mounts a button with text masked(******) on clicking this button the key( API key ) will be revealed which is a CopyToClipboard component )
const [hide, setHide] = useState(true);
If hide is true, I am rendering a Button ( spreading all the props )
<Button onClick={() => setHide(false)} {...props}>
******
</Button>
When this button is Clicked hide is false and I am rendering a CopyToClipboard component.
<CopyToClipboard
{...props}
>
{value}
</CopyToClipboard>
The Problem
In the above scenario, I am spreading {...props} to both Button and CopyToClipboard components.
But some props of CopyToClipboard are not compatible with that of Button's.
Fix
So at the top of the component destructure the props that are specific to a component (here CopyToClipboard).
Now safely spread the rest of the props to both the components and pass the new prop separately ( to CopyToClipboard component )
const {onCopy, ...restProps} = props
<Button onClick={() => setHide(false)} {...restProps}>
******
</Button>
<CopyToClipboard
onCopy={onCopy}
{...props}
>
{value}
</CopyToClipboard>
I am trying to access the props in my child component, I am rendering a grid row using a mapped json result:
getRowNodes: function() {
return this.props.contacts.map(function(contact){
return <Row
key={contact.id}
contact={contact}
columns={this.props.children} />;
}.bind(this));
}
When I render the component I can console log {this.props.data} and see all the properties, I can also see all the properties in chrome dev tools, however, when I try and access a property this.props.data.propertyName I get undefined.
If I try and access any of the properties below I get an error..any ideas?
Like Kirill Slatin said: You have to wrap it.
Try this:
getRowNodes() {
return (
<div>
{this.props.contacts.map(this._getRow)}
</div>
);
},
_getRow(contact) {
return (
<Row
key={contact.id}
contact={contact}
columns={this.props.children} />
);
}
NOTE: I have optimized the readability by using JSX Syntax.