Passing data between components using reactjs - reactjs

I have a div like following.
<div className="horiz_center" onClick={this.textType.bind(this,'text')}>
<img src={StarIcon} className="post_type_icon"/>
<a className="post_type_text">Text</a>
</div>
I can get the value with this function
textType(postType) {
this.setState({postType});
}
My question is if i want to use postType in another component, how can i pass it to that component?

const {postType} = this.state;
<MyComponent postType={postType} />
And in your component ( MyComponent ) access your properties like
this.props.postType

Related

How to use the same component with different classnames and import and use them in another component with different classnames?

I am trying to use same component with different props, like classnames, maybe I don't know the syntax or maybe this is impossible. I am trying to do something like this:
const MyComp = (class) => (
<div className={class}>
...
</div>
)
return (
<MyComp class="icon" />
)
Using in another component:
const List = ({MyComp}) => {
...
return(
<div>
{MyComp} // I am asking whether here or before what should I do to change the classname
</>
)
}
Or importing comp like this is not possible(it gives expected a string error when I use like <MyComp class="anotherStyle" />)?
You can do something like this!!
Distruct the props so you can get direct access to it inside that component.
const MyComp = ({class}) => (
<div className={class}>
...
</div>
)
return (
<MyComp class="icon" />
)

How to add a CSS class to an element with React function components

i would like to know how can i add a CSS class to an element which has no any CSS classes.I am looking React Functional component solution with Hooks. Here i want to add class to tag and i don't need to add ${myclass} in advance. That means tag should be without any attributes before we execute the addclass functionality. I tried the following method and needs to get a best practice on it. Thanks in advance!
function Trial(){
const [myclass, changeclass] = useState("");
const addclass=()=>{
changeclass(`active`)
}
return(
<div>
<h1 className={` ${myclass}`}>Hi</h1>
<button onClick={addclass}>Click it</button>
</div>
)
}
You can add an id and then manipulate the element in the DOM by queryselector
in the element you want to change its className
<h1 className={`${myclass}`} id = "change-class">Hi</h1>
and then in the function
const addclass=()=>{
document.querySelector("#change-class").classList.add('new-class-name');
}
so the whole code would look like this
function Trial(){
const addclass=()=>{
document.querySelector("#change-class").classList.add('new-class-name');
}
return(
<div>
<h1 className={`${myclass}`} id = "change-class" >Hi</h1>
<button onClick={addclass}>Click it</button>
</div>
)
}
For more info check here and here
You can use useRef() hook for this:
function Trial(){
const ref = useRef(null);
const addclass=()=>{
const h1 = ref.current; // corresponding DOM node
h1.className = "active";
}
return(
<div>
<h1 ref={ref} className="">Hi</h1>
<button onClick={addclass}>Click it</button>
</div>
)
}

Is it possible to render a react class when given its name as a string?

Here's a smaller example of what I'm trying to do, I don't know if it's possible to do something similar or I should use an entirely different method.
import {Design1, Design2} from './page-designs';
let designs = {
"page1":"Design1",
"page2":"Design2",
"page3":"Design1",
"page4":"Design2"
}
class DesignedPage extends React.Component {
let Design = designs[this.props.page]
render(){
return(
<div className="row flex-fill d-flex">
<div className="col-1"></div>
<Design /* This is the line that fails */
data = {this.props.data}
/>
</div>
</div>
)}
}
class Main extends React.Component {
render(){
return(
<DesignedPage
page = {this.props.openPage} /*this could be any of page1-4 depending on button a click*/
data = {this.props.data}
/>
)}
}
Ideally this would render the react elements Design1 or Design2 based on what props.page is passed, but instead it returns
"Warning: <Design1 /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements." and "The tag <Design1> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter."
I've thought of making a long if, elseif, elseif.. statement in DesignedPage (the actual code has many more than 2 designs), which I'm fairly confident would work, but looks very messy in comparison.
You can't render the component name by getting its name as a string. You need to map the string to the component iteself:
let designs = {
"page1":Design1,
"page2":Design2,
}
If you pass a string, react would think it's a HTML tag, hence it say'Design1' tag is unrecognised. Also, you could import the components and use them as values in the designs object in place of strings.
let designs = {
"page1":Design1,
"page2":Design2,
"page3":Design1,
"page4":Design2
}
make one function that return react component..
getComponent = ({data, pageName}) => {
if(pageName === "page1") return <Desig1 />;
if(pageName === "page2") return <Design2 />;
}
and call function from render of DesignedPage component
const {page, data} = this.props;
return(
<div className="row flex-fill d-flex">
<div className="col-1">
{getComponent(page, data)}
</div>
</div>
)

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

Dynamically style a React element

I am trying to dynamically style a React element: In the nav bar, I would like to make the font color of the <div> displaying the current page to be different from the other <div>s representing other pages. I have passed into my <NavBar> component's props the params representing the current page.
From this information, I have figured out a way to achieve what I am trying to do: I store the <div>s representing the page links in an object and wrap the selected page in another <div> carrying a 'selected'class name.
However, I could envision scenarios where this solution would disrupt styling in place since it adds a wrapper element and classes within the wrapper element would take precedence over the 'selected' class.
Does anyone know a better way? I have posted my solution and the context below:
export default class NavBar extends React.Component {
constructor(props) {
super(props);
}
render() {
const pages = {
my_subscribed_docs:
<div className='nav__row2-item'>Documents I'm Subscribed To</div>,
my_created_docs:
<div className='nav__row2-item'>Documents I've Created</div>,
new_doc:
<div className='nav__row2-item'>
<div className="plus-icon">+</div>
<div className='nav__row2-text_left-of-icon'>New Document</div>
</div>,
};
const currentPage = this.props.path.slice(1);
pages[currentPage] =
<div className='nav__row2-item--selected'>{pages[currentPage]}</div>;
debugger;
return (
<nav>
<div className="nav__row1">
<div className="nav__logo">Worksheet Generator</div>
<div style={{cursor: 'pointer'}} onClick={this.props.logout}>
Logout
</div>
</div>
<div className="nav__row2" >
{ Object.values(pages).map( page => (
<div key={shortid.generate()}>{page}</div>)
) }
</div>
</nav>
);
}
}
If you want to dynamically add a class to your component, I recommend using classnames library. This is pretty much the way to do it when using external stylesheets.

Resources