Passing multiple props to React component - javascript

I am trying to pass two argument using props in ImageText component.
I am not sure if it is right method or I have to create a map and then pass it.
import React, { PropTypes, Component } from 'react'
const ImageText = () => (
<div className="img-with-text">
<img className="img" src={props.imageUrl} />
<p className="txt">{props.imageText}</p>
</div>
);
export default ImageText;
Calling this component from another as follows
<ImageText imageUrl="/js.com" imageText="food"/>
But is throwing error as
Uncaught (in promise) ReferenceError: props is not defined
at ImageText

In your case issue with you are using "Arrow functions" which needs to pass params inside brackets
const ImageText = () => (
Should be
const ImageText = (props) => (
Now
let props = {
imageUrl:"/js.com",
imageText:""food""
}
<ImageText {...props} />
Access inside ImageText like
{props.imageUrl} or {props.imageText}

When you define your component like that, you need to pass your props as parameters to the anonymous function:
const ImageText = ({imageUrl, imageText}) => (
... rest of the code ...
);

When using a functional component (when you don't use a class) you must pass the props as an argument into to the function.
You can pass as many props add you need into a component.
const ImageText = (props) => (...
If using a standard component (as a class) you would call a prop with
this.props

Passing down multiple props in reactJS can be done using either of following ways:
// in App.js
<Modal post= {postProps} user={userDetails}/>
// in Modal.js
const Modal = (props) => {
const title = props.post.title
const username = props.user.username
// rest of the code..
}
in above example props (in Modal.js) forms an object with keys named post and user, thus you can access them like objects.
Another way would be:
// in App.js
<Modal post={postProps}/>
// in Modal.js
const Modal = ({post}) => {
const title = post.title
// rest of the code..
}
in this example post object has been imported explicitly in Modal.js
Final Remark : With first approach it gets very clear looking at your code that what has been passed down as props from parent element and might be clear to read.
While with second approach i argue that, it is better to use in scenarios where its just one prop to pass, since you don't have to write props. everytime

you are passing the props to dump component. it's not react component. pass the props to dump as function argument.
> import React, { PropTypes, Component } from 'react'
>
> const ImageText = ({imageUrl, imageText}) => (
> <div className="img-with-text">
> <img className="img" src={imageUrl} />
> <p className="txt">{imageText}</p>
> </div> );
>
> export default ImageText;

Creating:
const ImageText = ({ imageUrl, imageText }) => (
<div className="img-with-text">
<img className="img" src={imageUrl} />
<p className="txt">{imageText}</p>
</div>
);
export default ImageText;
Using; when you have already defined imageUrl and imageText somewhere before the return keyword:
<ImageText {...{imageUrl, imageText}}

Related

Exporting a variable set in a function to another component React

I have some variables that are set within a function in one component of my react application that I need to reuse in other components.
I set the variables in component 1 like so (this is a much simplified version but captures the structure)
export default function Example() {
const [market, setMarket] = useState('');
return (
<button onClick={setMarket('1')}>Click 1</button>
<button onClick={setMarket('2')}>Click 2</button>
<button onClick={setMarket('3')}>Click 3</button> )}
How can I export the 'market' variable specifically, so that I can import it into another component (in a separate jsx file) and render as necessary. I know that I can just import the whole component, or set a variable outside of this function in component 1 and export it but I do not know how I would then conditionally set it based on which button is clicked.
Thank you
Hey #Milo there are different ways to use state value in another component.
First is props-
Create a component that passes values like-
const passValue = () => {
const [ value, setValue ] = useState("")
return (
)
}
While in the second component we get the value like-
const SecondComponent = ({value})=>{
return(
<div>
{value}
</div>
)
}
While Second method is to pass value using state and get it by useLocation in another component-
First Component like-
const FirstComponent = () =>{
return(
<div>
<Link to="/secondpage" state={{value:yourValue/state}}>Click Here</Link>
</div>
)
}
Second Component Like-
const Second Component = () => {
const {state} = useLocation()
return(
<div>{state}</div>
)
}
Hope these solution helps to solve your problem. If you still facing issue lemme know, i will help you.
Thanks

How can I make useEffect not return the initial value? I keep getting empty array when I'm trying to pass an array of objects into a component

I'm trying to pass in an array of registered users into a component, however I can't because it always renders the initial empty array before actually rendering the correct content. I've tried using useRef and it still does not work.
const Home = () => {
const nav = useNavigate()
const [userList, setUserList] = useState([]);
const [loggedInUser, setLoggedInUser] = useState({});
const [currentChat, setCurrentChat] = useState(undefined);
const [showMenu, setShowMenu] = useState(false);
useEffect(() => {
const setLoggedIn = async() => {
if (!localStorage.getItem('loggedInUser')) {
nav('/');
} else {
setLoggedInUser(await JSON.parse(localStorage.getItem('loggedInUser')))
}
}
setLoggedIn().catch(console.error);
}, [])
useEffect(() => {
const fetchUsers = async () => {
const data = await axios.get(`${allUsersRoute}/${loggedInUser._id}`);
setUserList(data.data);
}
fetchUsers().catch(console.error);
}, [loggedInUser._id])
console.log(userList);
return (
<div id='container'>
<div id='sidebar'>
<div>
<div id='home-header'>
<h1>DevsHelp</h1>
</div>
<Userlist props={userList}/>
</div>
</div>
)};
And here is the component I'm trying to render.
const Userlist = (props) => {
return (
<div>
<div id='home-header'>
<h1>DevsHelp</h1>
</div>
<div id='userlist'>
{props.map((prop) => {
{console.log(props.length)}
return (
<div className='user'>
<h3>{prop.username}</h3>
</div>
)
})}
</div>
</div>
)}
export default Userlist;
So basically, react returns .map is not a function, and I assume it's because the array goes in empty. I'm fairly new to React, so if anyone could help me, I would greatly appreciate it. Thanks!
The problem is that you are mapping over the props object, not the userList.
Try to do the following:
const Userlist = (props) => {
return (
<div>
<div id='home-header'>
<h1>DevsHelp</h1>
</div>
<div id='userlist'>
// use props.users.map instead of props.map
{props.users.map((user) => {
return (
<div className='user'>
<h3>{user.username}</h3>
</div>
)
})}
</div>
</div>
)}
export default Userlist;
and at Home component change the props of UserList to users, just to avoid confusion
<Userlist users={userList}/>
I wouldn't name props for a component "props", really:
<Userlist props={userList}/>
If you really want to, then at least inside Userlist, you would need to refer to the props object:
props.props.map...
Name your props to something that make sense to you, like for example "users". Then call props.users.map(user => {...})
A React component can take many props. When you want to access them inside the component, you need to do so by name. In your case, you defined Userlist like this:
function Userlist(props){...}
In this case, all props would have to be accessed via the props object. You defined a props value inside this object when you called <Userlist props={userList]} />
Personally, I always destructure props when I define a new component, like this:
function Userlist({users}) {...}
As a side note, your code would have worked if you had destructured the props object: function Userlist({props}) {...} This would be the smallest change you could do to make the code work, I guess. But again, I would not use "props" as a name for a prop.

What is the React way of inserting an icon into another component?

I'm trying to create an WithIcon wrapper component which would insert a child (icon) into a wrapped component.
Let's say I have a button:
<Button>Add item</Button>
I want to create a component WithIcon which will be used like this:
<WithIcon i="plus"><Button>Add item</Button></WithIcon>
Ultimately what I want to achieve is this:
<Button className="with-icon"><i className="me-2 bi bi-{icon}"></i>Add item</Button>
Notice the added className and the tag within the Button's body.
I'm trying to figure out how the WithIcon component's code should look like. What is the React way of achieving this result?
The hardest part was the rules of using the WithIcon Will we only have one ?
Will we have only it at the leftmost ? Something like that.
But if we follow your example. We can relatively write something like this for the WithIcon
const WithIcon = ({ i, children }) => {
return React.Children.map(children, (child) => {
return (
<>
<i className={`me-2 bi bi-${i}`}></i>
{React.cloneElement(child, { className: "with-icon" })}
</>
);
});
};
Then we can just use it the way you want it
<WithIcon i="plus"><Button>Add item</Button></WithIcon>
What we do is just looping through the children which in react is any nested jsx you throw in it (Button in our case)
You can find my fiddle here : https://codesandbox.io/s/react-font-awesome-forked-321tz?file=/src/index.js
UPDATE
So my previous answer does not fully meet the end result we want. The will need to be the main parent
The idea is still quite the same as before but here we are infering the type of the component we passed inside the WithIcon This also adds a safeguard when we passed a nested component inside the WithIcon
const WithIcon = ({ i, children }) => {
return React.Children.map(children, (child) => {
const MyType = child.type; // So we can get the Button
return (
<MyType className="with-icon">
<i className={`me-2 bi bi-${i}`}></i>
{(React.cloneElement(child, {}), [child.props.children])}
</MyType>
);
});
};
I think I'll go to sleep I'll update the rest of the explanation at later date.
See the fiddle here :
https://codesandbox.io/s/react-font-awesome-forked-y43fx?file=/src/components/WithIcon.js
Note that this code does not preserved the other props of the passed component, but you can relatively add that by adding {...child.props} at the MyComponent which is just (reflection like?) of infering the component.
Of course also have another option like HOC Enhancers to do this but that adds a bit of complexity to your how to declare your component api. So Pick whats best for ya buddy
Maybe try using a higher order component?
const withIcon = (icon, Component) => ({children, ...props}) => {
return (
<Component className="with-icon" {...props}>
<i className=`me-2 bi bi-${icon}` />
{children}
</Component>
);
}
Then the usage is
const ButtonWithIcon = withIcon("your-icon", Button);
<ButtonWithIcon>Add Item</ButtonWithIcon>
From my experience with react it usually comes down to either using a property inside the component like here (https://material-ui.com/api/button/) or higher order component like what I described.
There are two common patterns used in React for achieving this kind of composition:
Higher-Order Components
Start by defining a component for your button:
const Button = ({ className, children }) => (
<button className={className}>{children}</button>
);
Then the higher-order component can be implemented like this:
const withIcon = (Component) => ({ i, className = '', children, ...props }) => (
<Component {...props} className={`${className} with-icon`}>
<i className={`me-2 bi bi-${i}`} />
{children}
</Component>
);
Usage:
const ButtonWithIcon = withIcon(Button);
<ButtonWithIcon i="plus">Add Item</ButtonWithIcon>
Context
Start by defining the context provider for the icon:
import { createContext } from 'react';
const Icon = createContext('');
const IconProvider = ({ i, children }) => (
<Icon.Provider value={i}>{children}</Icon.Provider>
);
and then your component:
import { useContext } from 'react';
const Button = ({ className = '', children }) => {
const i = useContext(Icon);
if (i) {
className += ' with-icon';
children = (
<>
<i className={`me-2 bi bi-${i}`} />
{children}
</>
);
}
return (
<button className={className}>{children}</button>
);
};
Usage:
<IconProvider i="plus"><Button>Add Item</Button></IconProvider>

Objects are not valid as a React child with React.memo

I am receiving the following errors
Warning: memo: The first argument must be a component. Instead received: object
Uncaught Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, compare}). If you meant to render a collection of children, use an array instead.
They happen when I change this component
const Tab = () => onLastTab
? <AccountTab data={data.account} />
: <InfoTab data={data.info} />
To be this component, the only difference is the use of React.memo
const Tab = () => onLastTab
? React.memo(<TabOne data={data.one} />)
: React.memo(<TabTwo data={data.two} />)
Those components wrapped in React.memo are definately just functional components that look like
const TabOne = ({data}) => (
<div>
<div className='d-flex '>
...
</div>
</div>
)
Why would this be happening? What can I do to stop it?
As the error message explains, you need to pass component to the React.memo(), not an object. TabOne is obviously a component name but you already created an object of that component and passed it through the React.memo().
You need fix your code as follows:
const TabOne = ({data}) => (
<div>
<div className='d-flex '>
...
</div>
</div>
)
export default React.memo(TabOne)
const Tab = () => onLastTab
? <TabOne data={data.one} />
: <TabTwo data={data.two} />

How to pass React Component as props to another component

I am trying to call PopupDialog.tsx inside Content.tsx as a sibling of Item.tsx.
Previously PopupDialog.tsx is called inside C.tsx file but due to z index issue i am trying to bring it out and call it in Content.tsx
Is it possible to somehow pass the whole component(popupDialog and its parameters) in Content.tsx so that i could avoid passing back and forth the parameters needed for popupdialog in content.tsx.
Code in C.tsx where PopupDialog component is called.
const C = (props: Props) => (
<>
{props.additionalInfo ? (
<div className="infoButton">
<PopupDialog // need to take this code out and want to add in Content.tsx
icon="info"
callback={props.callback}
position={Position.Right}
>
<div className="popuplist">{props.additionalInfo}</div>
</PopupDialog>
</div>
) : (
<Button className="iconbutton"/>
)}
</>
);
Content.tsx where i would like to call PopupDialog.tsx with its parameters
const Content = (props: Props) => {
const [componentToRender, docomponentToRender] = React.useState(null);
const [isAnimDone, doAnim] = React.useState(false);
return (
<div className="ContentItems">
<PWheel agent={props.agent} />
{isAnimDone && (
<>
<Item {props.agent} />
{componentToRender &&
<PopupDialog/> //want to call here with all its parameters to be passed
}
</>
)}
</div>
);
};
Folder Structure
App.tsx
->ViewPort.tsx
->Content.tsx
->PWheel.tsx
->Item.tsx
->A.tsx
->B.tsx
->C.tsx
{props.additionalinfo &&
->PopupDialog.tsx
->PopupDialog.tsx
So if I understand the question correctly you want to pass one component into another so that you can use the properties or data of the passed componenet in your current component.
So there are three ways to achieve this.
1)Sending the data or entire component as prop.This brings disadvantage that even though components which don't require knowledge
about the passed component will also have to ask as a prop.So this is bascially prop drilling.
2)The other is you can use context api.So context api is a way to maintain global state variale.so if you follow this approach you don't need to pass data or componenet as props.Wherever you need the data you can inport context object and use it in componenet.
3)Using Redux library.This is similar to context api but only disadavantage is that we will have to write lot of code to implement this.Redux is a javascript library.
Let me know if you need more info.
You need to :
<>
<Item {props.agent} />
{componentToRender &&
<PopupDialog abc={componentToRender} /> //you must call in this component, in this case i name it is abc , i pass componentToRender state to it
}
</>
and then PopupDialog will receive componentToRender as abc, in PopupDialog , you just need to call props.abc and done .
If you need to know more about prop and component you can see it here
I think what you want to use is Higher-Order-Components (HOC).
The basic usage is:
const EnhancedComponent = higherOrderComponent(WrappedComponent);
Below is such an implementation that takes a component (with all its props) as a parameter:
import React, { Component } from "react";
const Content = WrappedComponent => {
return class Content extends Component {
render() {
return (
<>
{/* Your Content component comes here */}
<WrappedComponent {...this.props} />
</>
);
}
};
};
export default Content;
Here is the link for higher-order-components on React docs: https://reactjs.org/docs/higher-order-components.html
Make use of
useContext()
Follow this for details:
React Use Context Hook

Categories

Resources