React tailwind, cannot pass tailwind css from parent to child - reactjs

I am running into a simple issue that doesn't seem to have an answer on quick google search or Tailwind doc.
I am a Vuejs user but I have started learning React. I have opted to use TailwindCSS for testing my React application but I noticed there is some differences of Tailwind usage between Vuejs and React.
In Vue, I can control a child component via the parent component like so:
Parent component:
<template>
<div class="w-screen">
<ChildComponent class="w-1/2 mx-auto" />
</div>
</template>
With the child being able to centre on screen through the above parent component as should in the ChildComponent's class.
However, when I tried to do the same in React like so:
Parent component:
import Homepage from './views/Homepage';
function App() {
return (
<div className='bg-black w-screen'>
<Homepage className="w-1/2 mx-auto"/>
</div>
);
}
export default App;
Nothin happens when I placed the CSS at the Homepage child component from the parent.
I am sure there is a simple answer but I wasn't about to search the doc or use any keywords to find this problem. Anyone got a hint or confirm this is intended in React or have I done something wrong with the installation?

This is less of a Tailwind question and more of a React question. You cannot use className on the Homepage component without passing it as a prop. In your case, Homepage is not expecting any className. So while making your Homepage component you have to provide a prop called 'className' then it will work fine.
Or if you simply use a div in place of Homepage it will work normally. Have a look at this codesandbox link

You need to consider that <Homepage/> is a React component and cannot accept HTMLAttrs just like that.
this example might clear it:
const app = () => {
<div className="bg-black">
<Homepage className="bg-red" />
</div>
}
const homePage = (props) => {
<div className={props.className}>
<h1 className="bg-red">hi</h1>
</div>
}
the className that you pass to <Homepage/> is actually a props rather than Html attribure.

In Vue it's fairly straightforward but in react you need to be explicit and use className in your component
// Creating component
const Button = ({ className, children }) => {
return <button className={`${className} bg-red-500`}>{children}</button>
}
export default Button
// Using component
<Button className="text-white">MyButton</Button>

import Homepage from './views/Homepage';
function App() {
return (
<div className='bg-black w-screen'>
<Homepage className="w-1/2 mx-auto"/>
</div>
);
}
export default App;
views/Homepage
you have to receive props that are going to be passed as className
const homePage = ({className}) => {
<div className={className}>
<h1 className="bg-red">hi</h1>
</div>
}
export default homePage
then export your component

Related

How can i unmount a functional component from DOM on click of a Button

I would like to "Unmount a simple Functional Component" from the DOM. I searched a lot and saw most of the tutorials are based on Class Components and I did'nt see any simple example on it. My requirement is Unmounting a Functional component from the DOM on click on a button. Following is the component with the button which i likes to unmount when click on it. Hopes someone can help me to do it. Thanks in Advance !
import React from 'react'
function App() {
return (
<div className='app-component'>
<h2 className="h2">App Component</h2>
<button>Unmount This Component</button>
</div>
)
}
export default App
If you want to unmount a component then you can use conditional rendering where you can declare state in parent component and based on the state you can mount or unmount component as:
This is the parent component from where you want to mount or unmount
CODESANDBOX DEMO
If you want to toggle component once then you can do the following because there is only one way to change state i.e from Test component. If you unmount this component there is no way to mount it again. So you can also declare button in App component from where you can mount or unmount on click of a button. CODESANDBOX
Parent component
export default function App() {
const [isShowing, setIsShowing] = useState(true); // STATE
return (
<div className="App">
{isShowing && <Test setIsShowing={setIsShowing} />}
</div>
);
}
Child component
function Test({ setIsShowing }) {
function unmountComponent() {
setIsShowing(false);
}
return (
<div className="app-component">
<h2 className="h2">App Component</h2>
<button onClick={unmountComponent}>Unmount This Component</button>
</div>
);
}
You can use state flag for removing element like this:
import React from 'react'
function App() {
const [flag,setFlage]=useState(true);
return (
<div className='app-component'>
{flag?<h2 className="h2">App Component</h2>:null}
<button onClick={()=>setFlag(!flage)} >Unmount This Component</button>
</div>
)
}
export default App

Can two React distinct and mounted components communicate with each other using props?

A simple example for explaining my question:
ButtonComponent is a component mounted on <div id="btn-container"> element
ModalComponent is a component mounted on <div id="modal-container"> element
So two components mounted on different elements in different places in the page, not sharing a common React element.
ModalComponent receive a prop named isOpen to trigger its visibility:
<ModalComponent isOpened={isOpened} />
The ButtonComponent should, in some way, pass the prop isOpen to the ModalComponent without sharing the same "root" component. Is this even possible?
Of course the "normal way" to do this would be a ButtonPlusModalComponent like the following:
export const ButtonPlusModalComponent = () => {
const [isOpened, setIsOpened] = useState(false);
return (
<>
<ButtonComponent onClick={() => setIsOpened(true)} />
<ModalComponent isOpened={isOpened} />
</>
);
};
I'm using React with a regular PHP application, so... not a complete "full" React application, only some parts and portions of the page are React components, no router at all.
So, I have that button somewhere in the page. That button should open the modal component, which is a... React component placed elsewhere in the page.
EDIT: explain why the question.
You must both component to share a common parent, thats how React works as Data Flows Down.
If you can, couple them into the same tree and use common solutions like Context API.
But, you describing a situation where the components are decoupled, so in order to have a common parent and mount a component into another HTML element, you need to use React.Portal.
Therefore you need one component to mount the other using Portals:
<body>
<div id="modal-container"></div>
<div id="btn-container"></div>
</body>
const Modal = ({ open }) => open && <>Modal</>;
const App = () => {
const [open, toggleOpen] = useReducer((p) => !p, true);
return (
<>
<button onClick={toggleOpen}>toggle</button>
{ReactDOM.createPortal(
<Modal open={open} />,
document.getElementById("btn-container")
)}
</>
);
};
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("modal-container")
);

My components are not being rendered when I click a link that should load them

I'm confused as to why nothing happens when I'm clicking links in my app.
In my index.js file, I am loading my main screen called 'Game'. Inside 'Game', I have two links, that when clicked, should render another screen.
In my index.js:
import React from "react";
import ReactDOM from "react-dom";
import Game from "./Game/Game";
ReactDOM.render(
<React.Fragment>
<Game/>
</React.Fragment>,
document.getElementById('gameContainer')
)
In my index.html:
<div>
<div id="gameContainer"></div>
</div>
<div id="root"></div>
My Game.js:
import React from "react";
import CharacterStats from "../CharacterStats";
import DungeonStats from "../DungeonStats";
const characterStatsComponent = () => {
return (
<CharacterStats />
);
}
const dungeonStatsComponent = () => {
return (
<DungeonStats />
);
}
const Game = () => (
<div>
<a id="showCharacter" href="#" onClick={characterStatsComponent}>Show your character</a>
</div>
<br />
<div>
<a id="showDungeon" href="#" onClick={dungeonStatsComponent}>Show current dungeon</a>
</div>
);
export default Game;
The two other components, CharacterStats and DungeonStats are just a few bits of html and reactjs to show some data.
Neither CharacterStats or DungeonStats are loading when I'm clicking the links.
I am also getting no errors in the console.
Nothing happens when the links are clicked.
I also put this inside each onClick event:
console.log('link was clicked');
And it does show the message in the console. So that shows that it knows the link is being clicked.
Is there anything that would prevent them from being loaded?
Thanks!
It wont work because you are returning jsx into the onClick function context, and not into the Game component's return value.
You could define a state using useState, something like showDungeon and showCharacter that defaults to false, change it to true onClick, and in the Game component's return value add:
{ showDungeon && <DungeonStats /> }
React uses something called Synthetic Events to achieve cross browser event handling. If I understood your question correctly than changing the onclick to onClick should do the job for you.

how can I dynamically attach sidebar components to multiple instances of sidebar?

I'm new to React and building out a design a ran into a problem.
I have a component called SideBar. I am using this component two times, one on each side of the page.
The problem is that I would like to add different components to each instance of the SideBar component. These would be lists of various items and etc. I assumed I could next component tags but the sidebar component doesn't output.
import React, { Component } from "react";
import SideBar from "./WorkspaceComponents/SideBar";
import ScrollerBox from "./WorkspaceComponents/SideBarComponents/ScrollerBox";
class Workspace extends Component {
render() {
return (
<main className="reely-workspace">
<SideBar position="SideBarLeft">
<ScrollerBox />
</SideBar>
<SideBar position="SideBarRight" />
</main>
);
}
}
export default Workspace;
Your sidebar component should receive a children prop and render it out.
Something like this:
class Sidebar extends Component {
render() {
const {children} = this.props;
return (
<div className="sidebar">
<h1>Sidebar</h1>
{children}
</div>
)
}
}
Check out this post on react docs to understand how to compose react components: https://reactjs.org/docs/composition-vs-inheritance.html
You can make your SideBar Component a wrapper component which wraps around the content given in it.
Making SideBar Component a Wrapper Component :
class Sidebar extends Component {
render() {
return (
<div className="sidebar">
// You can add any custom element here //
{this.props.children}
</div>
)
}
}
All your element passed inside the SideBar Component will now be rendered as a part of SideBar along with what it contains.
Way to consume the wrapper component:
<SideBar>
<Content1></Content1>
<Content2></Content2>
<Content3></Content3>
</SideBar>

Meteor + React + createcontainer

Using react with meteor here, I have main component called App, and it wraps page layout (Header, Sidebar, Right-sidebar).
export default class App extends Component {
render() {
return (
<div>
<nav className="navigation">
<Header />
<Sidebar />
</nav>
<div className="content">
<Subnavbar />
<div className="container">
{this.props.children}
</div>
</div>
<Rightsidebar />
</div>
);
}
};
I'm trying to setup authentication system using Meteor's built in auth system. using "accounts-password" package.
To my knowldge, I need to use createContainer from 'meteor/react-meteor-data' to inject auth params to components.
Similar to this example:
import { createContainer } from 'meteor/react-meteor-data';
import MainPage from '../pages/MainPage.jsx'
export default MainContainer = createContainer(({params}) => {
const currentUser = Meteor.user();
return {
currentUser,
};
}, MainPage);
However in the above example, it only injects the parms to a single component, how can I go about injecting auth info to all components in my app (Header, Sidebars ..etc)
Your help is highly appreciated.
Thank you
If you wrap App in createContainer, then App will have a prop currentUser. It can then be the responsibility of App to pass the currentUser prop to all of your components. If you find yourself passing around currentUser far too much, then you can wrap only the components that need currentUser in createContainer.
In that case you would have HeaderContainer, SidebarContainer, etc, each being wrapped with createContainer.

Resources