React Component vs Element for reusability in the same file - reactjs

Within the same file, I'm having an element (or group of elements) that I want to convert to something reusable. But because it is very small, I don't want to create a new file.
Ex.
// Article.js
const Article = props => {
const { name, tel } = props
return (
<div>
<section>
<p>Content A</p>
<small>Contact {name} now, via {tel}</small>
</section>
<small>Contact {name} now, via {tel}</small>
<div>
Having questions?
<small>Contact {name} now, via {tel}</small>
</div>
</div>
)
}
As you can see here, <small>Contact {name} now, via {tel}</small> is being used many times in Article component. So I want to convert it to something reusable, either a Component or just a JSX element.
The key points are:
I do not want to create a new file, because it will never being used by other components.
It needs to display content depending on name and tel variables.
It does not need to be flexible, the name and tel will always be the same as from Article props.
So I tried to achieve my goal with:
Option 1: Separated React Component (in the same file)
// Article.js
const Contact = props => {
const { name, tel } = props
return <small>Contact {name} now, via {tel}</small>
}
const Article = props => {
const { name, tel } = props
return (
<div>
<section>
<p>Content A</p>
<Contact name={name} tel={tel}/>
</section>
<Contact name={name} tel={tel}/>
<div>
Having questions?
<Contact name={name} tel={tel}/>
</div>
</div>
)
}
But from my key point number 3, I find this option redundant because I have no need to make Contact accepting props. Since this component will always display the same name and tel as from Article. So I come up with Option 2.
Option 2: React Component in React Component
// Article.js
const Article = props => {
const { name, tel } = props
const Contact = () => (
<small>Contact {name} now, via {tel}</small>
)
return (
<div>
<section>
<p>Content A</p>
<Contact />
</section>
<Contact />
<div>
Having questions?
<Contact />
</div>
</div>
)
}
The Contact component is shorter because it accepts no props. But the question is if it does not accepting props, shouldn't I just write it as a JSX element? So I come up with Option 3.
Option 3: JSX Element in React Component
// Article.js
const Article = props => {
const { name, tel } = props
const renderContact = <small>Contact {name} now, via {tel}</small>
return (
<div>
<section>
<p>Content A</p>
{renderContact}
</section>
{renderContact}
<div>
Having questions?
{renderContact}
</div>
</div>
)
}
This one I don't know how to name it properly (renderContact, contactElement, or contact?) because I have not seen it much.
Every option is working, but I want to know the differences between these styles,
Performance - how does each one work / how does it affect the perf?
Pros and Cons
Limitations - is there anything to worry when using it?
Popularities - what is the standard way of writing React?

In this situation we write a reusable function usually called renderer
const Article = props => {
const { name, tel } = props
const renderer=(name,tel)=>{
return (
<small>Contact {name} now, via {tel}</small>
)
}
return (
<div>
<section>
<p>Content A</p>
{renderer(name,tel)}
</section>
</div>
</div>
)
}

Related

Expression statement is not assignment or call when looping through a list and trying to create a grid layout of components

I am trying to create a grid layout of video components but my IDE gives me a warning saying
Expression statement is not assignment or call
import React, {Fragment} from 'react';
import VideoClip from "../Video/VideoClip";
function SubjectView(props) {
let ids = ["RfKHsvF69VdjdMu6bdugsyRcjYpQXrpKd6iZHeEknCkY00",
"RfKHsvF69VdjdMu6bdugsyRcjYpQXrpKd2ipHeEknCkY00",
"RfKHsvF69Vdjdiu6bdugsyRcjYpQXrpKd2iZHeEknCkY00"
];
return (
<Fragment>
<div className="columns-3" >
{ids.map((id)=>{
<VideoClip id={id}/>
console.log(id)
})}
</div>
</Fragment>
);
}
export default SubjectView;
I see the IDs printed in the console but nothing renders.
The video component looks like
function VideoClip() {
let { id } = useParams();
return (
<div className="container mx-auto px-4">
<MuxPlayer
streamType="on-demand"
playbackId={id}
metadata={{
video_id: "video-id-54321",
video_title: "Test video title",
viewer_user_id: "user-id-007",
}}
/>
</div>
);
}
export default VideoClip
I am wondering if I am trying to create the components incorrectly. Is there a best practice when trying to achieve this?
You're not returning any value from ids.map
<>
<div className="columns-3" >
{ids.map((id)=><VideoClip id={id}/>)}
</div>
</>
One issue is you are not returning <VideoClip id={id}/> in map function in jsx. Also, if map is used - key needs to be set
return (
<Fragment>
<div className="columns-3">
{ids.map((id) => {
console.log(id);
return <VideoClip key={id} id={id} />;
})}
</div>
</Fragment>
);
Next issue is about VideoClip component parameter. Id needs to be extracted in that way in case it is passed as an attribute. Also, memoize the objects that you are passing down to a components. Metadata here, for example.
function VideoClip({ id }) {
const metadata = useMemo(() => {
return {
video_id: "video-id-54321",
video_title: "Test video title",
viewer_user_id: "user-id-007"
};
}, []);
return (
<div className="container mx-auto px-4">
<MuxPlayer streamType="on-demand" playbackId={id} metadata={metadata} />
</div>
);
}
Last thing - wrap your array in useMemo so this array will not cause crazy rerenders.
const ids = useMemo(
() => [
"RfKHsvF69VdjdMu6bdugsyRcjYpQXrpKd6iZHeEknCkY00",
"RfKHsvF69VdjdMu6bdugsyRcjYpQXrpKd2ipHeEknCkY00",
"RfKHsvF69Vdjdiu6bdugsyRcjYpQXrpKd2iZHeEknCkY00"
],
[]
);
Note: you will see x2 logs in the console in the Codesandbox due to <StrictMode> is set.

Rendering different html in reusable components

So I'm trying to make a reuseable component, which takes in an array and a variable, then i want to map through that array and return html.
But when i reuse this component, the html wont necesarly be the same each time i use it. For example:
Home Component
<div className='home'>
<Mappedarray array={list} pattern={pattern}/>
</div>
Account Component
<div className='account'>
<Mappedarray array={users} pattern={pattern}/>
</div>
function Mappedarray(props) {
const {array, pattern} = props
const arrayrow = array?.map(el=>{
return VARIABLE_HTML
})
}
So this is the basic set up, now for the VARIABLE_HTML, I want to return different html elements, for example, in the Home Component I want to return
<Link to={el.link}>
<p>{el.title}</p>
<i className={el.src}'></i>
</Link>
But for the User Component, I want to return
<div className='usercont'>
<img src={el.src}/>
<p>{el.title}</p>
</div>
I've been using a solution like passing a boolean variable to the component to determine what html should be used, but the component will get very messy and doesn't seem like a good solution.
For example, in the Home Component I would pass down:
<Mappedarray array={list} pattern={pattern} home={true} />
Then in the Mappedarray Component I would do
function Mappedarray(props) {
const {array, pattern, home, user} = props
const arrayrow = array?.map(el=>{
return <>
{
home?
<Link to={el.link}>
<p>{el.title}</p>
<i className={el.src}'></i>
</Link>
:user?
<div className='usercont'>
<img src={el.src}/>
<p>{el.title}</p>
</div>
:ANOTHER_VAR?
...
}
</>
})
}
Therefore, by doing it like this it would get very messy and disorganized, looking to a more dynamic way of doing this
Well since you are mapping trough same array and want different result maybe reusable component is not for this the best case. But if you want to have this united into one component like this you can just add a flag isLink to your reusable component and you are done:
function Mappedarray(props) {
const { array, pattern, isLink } = props;
const arrayrow = array?.map((el) => {
return isLink ? (
<Link to={el.link}>
<p>{el.title}</p>
<i className={el.src}></i>
</Link>
) : (
<div className="usercont">
<img src={el.src} />
<p>{el.title}</p>
</div>
);
});
}
Than this would be usage of that component in two cases:
Home Component
<div className='home'>
<Mappedarray array={list} pattern={pattern} isLink={true}/>
</div>
Account Component
<div className='account'>
<Mappedarray array={users} pattern={pattern} isLink={false}/>
</div>
NOTE
If you just put isLink with no ={true} it will be implicitly true. But for this example i added it explicitly
You can accept a render function as a prop. But if you are doing that then your Mappedarray isn't doing much of anything.
function Mappedarray({ array = [], render }) {
return (
<div>{array.map(render)}</div>
);
}
You can define render components for various types. Make sure that you are setting a key property since we will use this as a callback for .map.
const MyLink = ({ link, title, src }) => (
<Link to={link} key={link}>
<p>{title}</p>
<i className={src}></i>
</Link>
)
You would call Mappedarray by passing the function component as the render prop. Your array prop would be an array of props for that component.
const Test = () => {
return (
<Mappedarray
array={[{ link: "/", title: "home", src: "/images/home.jpg" }]}
render={MyLink}
/>
)
}
With Typescript Types
You could also tweak this slightly to pass the array index as a prop to the render component instead of passing it as a second argument. This version allows for both class components and function components to be passed to render.
function Mappedarray<T>({ array = [], render: Render }: Props<T>) {
return (
<div>{array.map((props, index) => (
<Render {...props} index={index} />
))}</div>
);
}
With Typescript Types

Create a custom Hook for splitting strings (React JS)

I'm working with React JS (hooks) for recently. For a project, I need to split many strings in different divs. So, I created a function to this, that saves me some time! My actual question is : Should I create a custom Hook instead of my function ?
Of course, the actual code works, but as a beginner, I don't know if my way is clean. I need feedbacks cause my main goal to write the best and clear code as possible.
// Splitting Texts
const splitText = str => {
return (
<div>
{str.split(' ').map((item, index) => {
return (
<div key={index}>
{item}
</div>
);
})}
</div>
);
};
// Main App
export default function App() {
const name = splitText('Lucie Bachman');
const description = splitText('Hey, this is my first post on StackOverflow!');
return (
<div className="App">
<h1>{name}</h1>
<h2>{description}</h2>
</div>
);
}
Expected results :
<h1>
<div>
<div>Lucie</div>
<div>Bachman</div>
</div>
</h1>
I'm super excited to have joined the community!
Thanks to you, and take care.
Lucie Bachman
A custom hook is something that uses out of the box react hooks to actually provide a logic and return data.
If the function returns JSX, its actually just a function or can be used as a functional component
Since you only want to split string once you can convert it into a component and use React.memo to optimize rendering
// Splitting Texts
const SplitText = React.memo(({str}) => {
return (
<div>
{str.split(' ').map((item, index) => {
return (
<div key={index}>
{item}
</div>
);
})}
</div>
);
});
// Main App
export default function App() {
return (
<div className="App">
<h1><SplitText str={'Lucie Bachman'} /></h1>
<h2><SplitText str={'Hey, this is my first post on StackOverflow!''} /></h2>
</div>
);
}

How to use a variable (a URL saved as a string) from another component?

Question: How can I use different URLs from different components in a third component as one ?
I am learning React.js and I would like to make multiple pages. This is an example project for learning purposes. There is one page where I can see the images and info of TeamA and there is another page where I can see the images and info of TeamB.
In order to avoid duplicates, I want to separate small components. I have a Card component which displays the image of team members with name and info. I want to use this one Card component for the page TeamA and also for the page TeamB.
The only difference now is the URL for the images - there is 1 URL for TeamA and one for TeamB. It should change accordingly.
I hope I could describe the problem. Please see the code examples below.
I appreciate your help. Have a nice day!
1st Component which has a unique URL for images:
const TeamA = ({id}) => {
const exampleImages = `https://url`;
return (
<div>
<Teams>
<Card teamImg={exampleImages}/>
</Teams>
</div>
);
}
2nd Component which also has a unique URL for images:
const TeamB = ({id}) => {
const exampleImagesB = `https://url`;
return (
<div>
<Teams>
<Card teamImg={exampleImagesB}/>
</Teams>
</div>
);
}
The Component that displays the information of the Team component (above). In this component, I want to use the URLs from the other components and add it to the img tag.
const Card = ({ name, email, id, teamImg }) => {
return (
<div className='tc'>
<img alt='image' src={`${teamImg}`}/>
<div>
<h2>{name}</h2>
<p>{info}</p>
</div>
</div>
);
}
You can make team a functional component of it's own:
const Team = ({id, imageUrl}) => {
return (
<div>
<Teams>
<Card teamImg={imageUrl}/>
</Teams>
</div>
);
}
Then you can pass imageUrl into your teams at a higher level without writing them out as separate components each time.
For example if you have your different urls as an array, this could be the component above team:
const AllTeams = () => {
const images = [{ id: 1, url: 'url1' }, { id: 2, url: 'url2' }];
return (
<>
{ images.map(image => <Team key={image.id} imageUrl={image.url} />) }
</>
);
}

Calling a function for ALL child components

I have 3 components. They parent layout, a select box, and a panel this is generated x times from some data.
<Layout>
<Dropdown>
<Panel>
<Panel>
<Panel>
I'm trying to make it so when the select value changes, the contents of each panel changes. The changes are made by doing some math between the new select value, and data that is stored in the panel component. Each panel has different data.
Layout.js
updateTrueCost(selected){
this.refs.panel.setTrueCost
}
render(){
return (
<div>
<div class="row">
Show me the true cost in
<CurrencyDrop currencyChange = {(e) => this.updateTrueCost(e)} data = {this.state.data} />
</div>
<div class="row">
{this.state.data.map((item, index) => (
<Panel ref="panel" key = {index} paneldata= {item} />
))}
</div>
</div>
);
}
Panel.js
setTrueCost(selected){
//Do some math
this.setState({truecost: mathresult})
}
render(){
return(
<div>
{this.state.truecost}
</div>
)
}
CurrencyDrop.js
onHandelChange(e){
this.props.currencyChange(e);
}
render(){
return(
<Select
onChange={this.onHandelChange.bind(this)}
options={options} />
)
}
The current result is only the last panel updates when the select changes. I'm guessing I'm doing something wrong with the ref handling, but I must not be searching the right terms because I can't find any related questions.
Instead of calling ref's method use React build-in lifecycle methods.
class Panel extends React.Component {
componentWillReceiveProps (newProps) {
// compare old and new data
// make some magic if data updates
if (this.props.panelData !== newProps.panelData) {
this.setState({trueCost: someMath()});
}
}
render () {
return <div>{this.state.trueCost}</div>
}
}
Then just change input props and all data will be updated automatically.

Resources