React Smooth Scroll into specific location in my reusable component? - reactjs

So I originally created a smooth scroll effect with regular HTML css and js, but I'm trying to convert it into react, but I'm not sure if I am doing it properly.
I have a Navbar component and just set the path
<NavLinks to='/#posts'>Posts</NavLinks>
Then I have my Main Content sections which are reusable components that receive data and display a different design based on what data I pass in.
function InfoSection({
lightBg,
id
}) {
return (
<>
<div
id={id} //Passed in the id value
className={lightBg ? 'home__hero-section' : 'home__hero-section darkBg'}
>
// Rest of my JSX is inside here
</div>
</>
);
}
Then in my Data file I'll pass in the values
export const homeObjThree = {
id: 'posts',
lightBg: true,
}
Then I display my reusable components in my index.js page
function Home() {
return (
<>
<InfoSection {...homeObjThree} />
</>
);
}
Then I import that into my App.js file
function App() {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => {
setIsOpen(!isOpen);
};
return (
<Router>
<Sidebar isOpen={isOpen} toggle={toggle} />
<Navbar toggle={toggle} />
<Home />
<Footer />
</Router>
);
}
So when I inspect, it shows the Home HTML with an id="posts" which I set from my data file, but when I click on the Post nav menu item it doesn't scroll down to the section. Nothing actually happens, so I can't tell if my approach even works for padding in ids into my data file or if I would need to write scroll functions to implement it.

Related

rendering a component inside a react page using Menu

I have a menu and the main body of the page. I have created different pages as components with some text. All I want is that when I click on the side bar menu, the components are displayed in the main page. How can I make this work?
const items2 = [{
label: 'Rice',
key: 'rice',
},
{
label: 'AB Test',
key: 'ab',
}]
const MainLayout = () => {
const {
token: { colorBgContainer },
} = theme.useToken();
const navigate = useNavigate();
const onClick = (e)=>{navigate(`/${e.key}`)}
return (
<Layout>
<Sider >
<Menu
mode="inline"
items={items2}
onClick = {onClick}
/>
</Sider>
<Content >
//I Want to open the pages here
</Content>
</Layout>
</Content>
To render a component inside other component, React provides a special props name children.
To achieve your requirement, you can do like this:
MainLayout.js:
export const MainLayout = ({children}) => {
const {
token: { colorBgContainer },
} = theme.useToken();
const navigate = useNavigate();
const onClick = (e)=>{navigate(`/${e.key}`)}
return (
<Layout>
<Sider>
<Menu
mode="inline"
items={items2}
onClick={onClick}
/>
</Sider>
<Content>
{children}
</Content>
</Layout>
)
}
In MainLayout.js, you only need to write {children} inside component <Content>, React will do the rest for you to pass the content of Rice or AB or whatever for you. In each page, just place <MainLayout> at the top of the outer of rest of your code.
Please see 2 example files below.
Rice.js:
import MainLayout from './MainLayout';
export const Rice = () => {
// Do some stuffs here...
return (
<MainLayout>
<div>
<h2>Best rated rice in the World</h2>
<ol>
<li>Pinipig</li>
<li>Riz de Camargue</li>
...
</ol>
<div>
</MainLayout>
)
}
Corn.js:
import MainLayout from './MainLayout';
export const Corn = () => {
// Do some stuffs here...
return (
<MainLayout>
<div>
<h2>Best Corn Side Dish Recipes</h2>
<ol>
<li>Whipped-Cream Corn Salad</li>
<li>Classic Grilled Corn on the Cob</li>
...
</ol>
<div>
</MainLayout>
)
}
You can read more and play around with the example code from React's official documents.
It is the basic concept of React, so before you start to build something big, I suggest to follow this docs first or find some series of React tutorials for beginner, they will explain key concepts of React so you would not save more time.
You need to use react-router-dom to navigate when you click other MenuItem. Create your own RouterProvider and put it in the Content.
<Content>
<RouterProvider router={router}>
</Content>
EDIT
Now you have two way to navigate to your Component. First is using Link and set it to your label.
{
label: <Link title="Rice" to="/rice">Rice</Link>,
key: 'rice',
}
Second way is hook useNavigate
const navigate = useNavigate();
const onClick = (e)=>{navigate(`/${e.key}`)}
//Add to Menu
<Menu
onClick={onClick}
//other props
/>

Change the state of another component using functionnal components in React

I have a button in my header that has to switch the state of the lateral menu of the page (to know if it has to be shown or not). I found out on Internet how to do so using Class Components, but not with Functional Components, do you have any idea on how to achieve that ?
Here's a simplification of my actual code in order to reproduce my issue (I removed all useless code).
App.js :
function App() {
return (
<div className="App">
<Header />
<div>
<LateralMenu />
</div>
</div>
);
}
The Header component :
function Header() {
const [lateralIsOpen, setLateralIsOpen] = useState(true);
function changeLateralMenu() {
setLateralIsOpen(!lateralIsOpen);
}
return (
<header>
<div onClick={ changeLateralMenu }>
</header>
);
}
And the LateralMenu component :
function Header() {
const [lateralIsOpen, setLateralIsOpen] = useState(true);
return (
<section>
{ lateralIsOpen ? "open" : "closed" }
</section>
);
}
I tried (but maybe not correctly) to declare the lateralIsOpen State in the App component and sending it through props to my children componenents (Header & LateralMenu).
I also tried looking at this question (and a few others) which is pretty similar, but don't see how I can apply it in my case because (as I understand) he uses a button in the parent component, that changes a state in the parent component, and then send it to the children through props... Where in my case, the button to switch it is already in a child.
I'd suggest you to move the state out of the LateralMenu to the parent (App) and then just pass the toggle function to the Header to toggle it.
export default function App() {
const [lateralIsOpen, setLateralIsOpen] = useState(true);
return (
<div className="App">
<Header toggleLateral={() => setLateralIsOpen((prev) => !prev)} />
<div>
<LateralMenu isOpen={lateralIsOpen} />
</div>
</div>
);
}
function Header({ toggleLateral }) {
function changeLateralMenu() {
toggleLateral();
}
return (
<header>
<div onClick={changeLateralMenu}>click</div>
</header>
);
}
function LateralMenu({ isOpen }) {
return <section>lateral is {isOpen ? 'open' : 'closed'}</section>;
}
Codesandbox: https://codesandbox.io/s/eager-heyrovsky-z75njd

How to re-render children in ReactJS?

Problem Context
I have a component named <Layout/> which contains my header and footer. It wraps all my routes as such:
<Layout>
<div className="container">
<Route path="/sign-in" exact component={SignIn}/>
<ProtectedRoute path="/" exact component={Home}/>
<ProtectedRoute path="/statistics" exact component={Statistics}/>
...
...
</div>
</Layout>
My <Layout/> component is defined as such:
const Layout = (props) => {
return (
<div>
<Header/>
{props.children}
<Footer/>
</div>
);
}
I did this so that i don't have to include my header and footer in each and every component.
Problem
In my header component, I am using the instance auth which indicates whether a user is logged in or not. The auth changes after the user signs in. However, even though the auth changes, my <Header/> component in the <Layout/> is not re-rendered. I have to manually refresh it to incorporate the changes.
My <Header/> component is defined as:
import auth from '../../auth';
const Header = () => {
return (
{auth.isAuth() ?
//render icon A
: null
}
<div>
Healthcare Management System //header title
</div>
{auth.isAuth() ?
//render icon B
: null
}
</div>
);
}
export default Header;
This is my auth class:
class Auth{
constructor(){
this.authenticated = JSON.parse(sessionStorage.getItem('profile'));
}
login(cb){
this.authenticated = JSON.parse(sessionStorage.getItem('profile'));
cb();
}
logout(cb){
this.authenticated = null;
cb();
}
isAuth(){
return this.authenticated;
}
}
export default new Auth();
What I require
What i want is supposedly simple; when the auth.isAuth() == null, show no icons and only the title (this behaves correctly). When, the auth.isAuth() != null, show icons A and B (this does not behave correctly and requires me to refresh the page in order to render the icons).
Somehow, i want the <Layout/> component to re-render once the auth changes. Thank you!
React component is only rerendered when its props or its state is changing, therefore you should put auth in a state which can be set as followed:
import auth from '../../auth';
const Layout = (props) => {
const [authenticated, setAuthenticated] = React.useState(false);
React.useEffect(() => {
if(!auth.isAuth()) {
setAuthenticated(false);
} else {
setAuthenticated(true);
}
}, []); // this useEffect logic is not working, you need to determine how you want to configure the condition in which authenticated state is set
return (
<div>
<Header authenticated={authenticated} />
{props.children}
<Footer/>
</div>
);
}
Header component:
const Header = ({ authenticated }) => {
return (
{authenticated ?
//render icon A
: null
}
<div>
Healthcare Management System //header title
</div>
{authenticated ?
//render icon B
: null
}
</div>
);
}
export default Header;

How can I change an image dynamically in Ionic React?

I'm pretty new to React and TypeScript, and I ran into this problem:
In my UI I'm using several decorative graphics as follows:
import Kitten from './img/Kitten.png';
<img className="Image" src={Kitten} />
Now, I have a dark-mode toggle. When it fires, I want to replace all images with their appropriate dark-mode version. I was thinking about something like this:
import Kitten from './img/Kitten.png';
import DarkKitten from './img/DarkKitten.png';
//gets called when dark mode is toggled on or off
const darkModeToggleFunc = () => {
document.querySelectorAll('.Image').forEach(element => {
if(element.src.includes("Dark")) {
element.src = element.src.replace("Dark", "");
} else{
element.src = "Dark" + element.src;
}
});
}
<img className="Image" src={Kitten} />
Now, in React I have two problems: the .src-attribute is unknown because element is not necessarily an image and the second problem is: I don't assign URIs as src but the variable from the import. So there isn't really a string I can change... If I'm informed correctly, React uses Base64 for images specified this way.
How could I achieve my goal in React?
Edit: App.tsx
//bunch of imports
const App: React.FC = () => {
return (
<IonApp>
<IonReactRouter>
<IonSplitPane contentId="main">
<Menu />
<IonRouterOutlet id="main">
<Route path="/page/:name" component={Page} exact />
<Redirect from="/" to="/page/Production" exact />
</IonRouterOutlet>
</IonSplitPane>
</IonReactRouter>
</IonApp>
);
};
export default App;
First things first when it comes to react you dont directly go and change things in the document level, you update the virtual DOM and let react take care of the rest.
You scenario is on changing the theme of the app, this answer is on using React context to change theme and use images appropriately.
First you create a Context which will hold the theme value
const AppContext = createContext({
theme: "light",
setTheme: (theme) => {}
});
Here we are going to use a state variable for simplicity, you can use anything you prefer.
Heres the app.js file
export default function App() {
const [theme, setTheme] = React.useState("light");
const themeState = { theme, setTheme };
return (
<AppContext.Provider value={themeState}>
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<ImageViewer />
<DarkModeSwitch />
</div>
</AppContext.Provider>
);
}
Here we set the theme value in the state and set the context to that, the setTheme can be used to update the theme from any component that is in the tree. in your case the darkmodeswitch, here we toggle the value
const DarkModeSwitch = () => {
const { theme, setTheme } = useContext(AppContext);
const darkModeToggle = () => {
setTheme(theme === "light" ? "dark" : "light");
};
return (
<div>
<input
type="checkbox"
checked={theme === "light"}
onChange={() => darkModeToggle()}
/>
</div>
);
};
Coming to your main requirement, the images, lets use a common files for images with the contents
export const Kitten ="image source 1";
export const KittenDark ="image source 2";
You simply set the image based on the theme like below
import { Kitten, KittenDark } from "./images";
export default function ImageViewer() {
const { theme } = useContext(AppContext);
return (
<img
alt="kitten"
style={{ height: 50, width: 100 }}
src={theme === "light" ? Kitten : KittenDark}
/>
);
}
as you can see everything is connected via the context and once you update the context you can see the images change.
You can see a working version here
https://codesandbox.io/s/react-theme-switch-3hvbg
This is not 'THE' way, this is one way of handling the requirement, you can use things like redux etc

Return checked box values to another React component containing textfield

I am new to React js. Can anyone tell me, how to grab the values of checked boxes into another component containing a textfield? I got those values in my console from checkedBoxI tried this https://jsfiddle.net/r7dm7wo0/ MenuList.js
const items = [
'one',
'two',
'three'
];
class MenuList extends React.Component {
componentWillMount () {
this.selectedCheckboxes = new Set();
}
toggleCheckbox = (label) => {
if (this.selectedCheckboxes.has(label)) {
this.selectedCheckboxes.delete(label);
} else {
this.selectedCheckboxes.add(label);
}
}
handleFormSubmit = (formSubmitEvent) => {
formSubmitEvent.preventDefault();
var checkedBox = new Set([...this.selectedCheckboxes].map(function(item){
return(item);
}));
console.log(checkedBox)
<MenuItem items={this.checkedBox}/>
}
createCheckbox = (label) => (
<Checkbox
label={label}
handleCheckboxChange={this.toggleCheckbox}
key={label}
/>
)
createCheckboxes = () => (
items.map(this.createCheckbox)
)
render() {
return (
<div className="background">
<div className="checkboxForm">
<div className=" col-lg-12">
<div className="col-lg-6 floatLeft">
<form onSubmit={this.handleFormSubmit}>
{this.createCheckboxes()}
<button className="btn btn-default" type="submit">Save</button>
</form>
</div>
</div>
</div>
</div>
)
}
}
MenuItem.js
class MenuItem extends React.Component {
render() {
const { items } = this.props
return(
<div>
<textarea>{items}</textarea>
</div>
)
}
}
MenuItem.propTypes = {
checkbox: PropTypes.string
}
index.js
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={IndexPage}/>
<Route path="login" component={LoginForm}/>
<Route path="menu-list" component={MenuList}/>
<Route path="menu-item" component={MenuItem}/>
</Router>
);
If you have data that is shared trough several pages, you have to save the information somewhere. You can save it:
server-side: usefull to syncronise multiple pages on multiple device but you a server data base
in cookies or forages: usefull to have the data shared between browser tabs and alive even after closing the browser.
in an application state: useful for data related to the current application and remove when the page is reloaded or when the browser is closed. It is often use for single app like React
In your case, I think you need an application state. I suggest you to use Redux that works fine with React.
Later, you'll be able to load data from server and fill the application state to have all your page have an access to the loaded information.
If you have data that is shared trough several components in the same page, you'll need to use a React state.

Resources