How to create new page without react router - reactjs

I am creating a react project where I have a button in my home page and when I click it, it should open up a new page that contains a form to fill. Because it is a form and it should only appear after clicking the button on the home page, I don't want to use react router because I don't want the user to just type 'mywebsite.com/form'. I also want the functionality that, when the user submits the form, the page then goes back to the homepage and the data from the form should be available in the home page. For example, if the form had text fields, lists, date pickers, etc. I want all that data in the homepage after the user submits the form.
P.S. I am using material-ui components for my whole app so the text fields, datepickers, etc. are all mui components, so the data fetching from these components has to be according to that.

In your Home component do a conditional render based on if the button was clicked. If it was, render your Form component. Ideally your Home component should be your smart component and the Form should be a dumb component. Meaning that Home should manage all the state and Form is purely just for visuals(UI).
import React, {useState, useEffect} from 'react';
import myForm from './myForm';
const Home = () => {
const [isBtnClicked, setIsBtnClicked] = useState(false);
const [formData, setFormData] = useState(undefined);
useEffect(() => {
if(formData){
setIsBtnClicked(false); // or you can pass this setter to form component and set to false when form is submitted.
}
},[formData])
return (
<>
{isBtnClicked
? <myForm setFormData={setFormData} />
: (<h2>Home With Button</h2>
<Button variant="contained" onClick={() => setIsBtnClicked(true)})
}
};
import React from 'react';
const myForm = ({setFormData}) => {
//assuming you have refs to all inputs
const handleFormSubmit = (e) => {
e.preventDefault();
// construct form data and call setFormData()
}
return (
<>
<form onSubmit={handleFormSubmit()}>
...
</form>
</>
};

Since you are using material-ui, I would recommend you use one of their Dialog components. In case you want to make it look like a completely different page opens up to fill the form, you could use their full-screen dialog component. Build your form within this component and change the props to your liking. Conditionally render the form when the user clicks a button on your home page.
Use the fullscreen prop from the Dialog API and add others that you need.

If i can give my opinion, i think you can use NextJS with server side rendering for this. NextJS provides a structured folder called "pages". You can add your page to this. The redirecting can also be done using router in next package. An easy way without any need for react router. I used to use react router until i found out about this.

Related

Passed the button text to state, but text reverted back during page refresh in React hooks

I have a record with two buttons, on click on Accept button should send the data back to server and display the text to Save and should not change the text afterwards ( should change only for clicked buttons).
In the below example, it is changing the text during on click, which is fine, but while refreshing the page it set back the text to Accept. Could someone please advise on how can I fix this ?
import { useState, useEffect } from "react";
import "./styles.css";
export default function App() {
const [acceptPlayer, setAcceptPlayer] = useState("Accept");
const postData = (e) => {
const text = "Save";
setAcceptPlayer(text);
};
return (
<div className="App">
<div>
<button onClick={postData} value="accept">
{acceptPlayer}
</button>
</div>
</div>
);
}
https://codesandbox.io/s/admiring-austin-1w1g38?file=/src/App.js
There is nothing wrong with this code. It's the expected behavior of the react. React or any other framework does not persist state on refresh That's the responsibility of the BE.
Solution:
When you can get API to show the data on the page, In the API response, you can return the data in the following format:
{
isAccepted: false,
//...something else
}
When you click on accept button and call the API BE is responsible for updating the isAccepted flag from the next time the API is called. You will get the following response:
{
isAccepted: true,
//...something else
}
Based on isAccepted flag you can toggle the button text.
Of course, it would go back to Accept because react only stores state locally. So every time you refresh the page it will go back to its initial state.
So if you want to get the updated state in your component then you have to fetch the latest data from the database in useEffect and have to set it to a state.

React opening react-bootstrap modal in content component from sidebar

I was wondering what would be best way to open a react-bootstrap modal from sidebar that is in another nested component?
To clarify a bit I have a modal an it is rendered in the component which lists items and it is used for edit them when you click one of the items. What i would like to do is when user clicks on the button in the sidebar that it opens that modal to add a item to the list (change from edit to add).
At first i was thinking to move the modal to parent of the sidebar but the problem is i would have to pass props 4 times to get to the list, and that could be confusing to someone who will later edit this code.
Next i taught about the context and sure that could work but to make a global state just for that it loses its purpose.
I taught about using refs but i am not sure on how to implement them.
And last thing what i taught about was just render the modal as a new component inside the sidebar but it wont change much since i want to update the list once user has added the item and i would be in the same spot.
And i would like to avoid directly accessing DOM with id (if possible), because i would like to find a "react way" of doing this.
For example (just to visualize not the actual code)
<Root>
<Component1>
<Component2>
<SideBar>
<Button onClick={setShowModal(true)}> <!-- click here -->
</SideBar>
</Component2>
<Component3>
<Component4>
<Modal show={showModal}/> <!-- open modal here -->
</Component4>
</Component3>
</Component1>
</Root>
I would just like a hint on how to approach this and what would be the best way to do this.
The most "React" way would be to store the state on the component that is the first common ancestor of the button and the modal. The downside to this approach, as you mentioned, is that you will have to pass this state down. I think this would still be a clean approach.
Another thing you could consider for this case is an event emitter. You have the Modal listen to an event to change its "open" state:
import EventEmitter from 'EventEmitter';
const eventEmitter = new EventEmitter();
export default function Modal() {
const [open, setOpen] = React.useState(false);
const handleToggleModal = () => {
setOpen(!open);
}
React.useEffect(() => {
eventEmitter.addListener('toggle-modal', handleToggleModal)
return () => {
eventEmitter.removeListener('toggle-modal', handleToggleModal);
}
}, [])
// ...
}
In the component that opens/closes the modal, you will then have to emit the corresponding event:
eventEmitter.emit('toggle-modal');

How to implement a custom cursor component in react

I'm trying to make a custom cursor component in react, based on this article: https://dev.to/andrewchmr/awesome-animated-cursor-with-react-hooks-5ec3
I'm using react router dom, and the problem is that the hover events are only working on the content within the router components on initial page load, or after refreshing the pages.
The hover is however always working on the nav component links. I've set up a basic CodeSandbox where you can see how the hover works on the nav links, but not on the page content links (after initial page load and when navigating between pages).
https://codesandbox.io/s/dazzling-newton-u9hk5
I'm a beginner with react, so I'm sure I'm going about this the wrong away. Can someone please help?
The issue here is that when the route changes, your Cursor Component does not need to be updated, therefore the useEffect hook is not called. And that's why your listeners are not attached to new anchor tags that appear when you change the route.
However, React Router Dom has introduced a new hook called useLocation that you can use to respond to the route change. This hook returns current url, so when you pass it to the array as below, useEffect will be called everytime when the url changes, therefore it will attach your listeners to the recently rendered anchor tags
Add this to your Cursor component:
import { useLocation } from "react-router-dom";
// then in your functional component
const location = useLocation();
useEffect(() => {
addEventListeners();
handleLinkHoverEvents();
return () => removeEventListeners();
// eslint-disable-next-line
}, [location]);

How do I dynamically switch between screens and update the url during search input

I have a search input box located in the header. When a user searches and clicks 'enter', an (callback) event is sent out to all of the relevant components that need to react to the search event, including the components that display the search results. My issue is that the header's search box would be visible on other non-search-result screens, and when I search there's no "clean" way of quickly mounting the search-result screens and displaying the search results (I hope it's not too confusing).
So the question is what type of approaches did you take to solve this issue? I was thinking of relying on window location and relying on React-router to load the search-results screen. Then looking at the query parameter (or path that contains the search query) and then kicking off the search.
Update (for clarity):
Go to https://www.brainyninja.com/podcast/78b7ab84cf98735fbadb41bb634320f8 The body component name is
Now type any other search term in the header's search box and click enter
The body component that displays search results is . I need to navigate to the /search route in order to load the component. The only way I figured out how to do that is by doing a 'window.location = "/search/?query=somesearchquery"' command, which reloads the whole page and negates the point of having an SPA. I don't know of any cleaner way of changing the current body component
Any suggestions?
Found my answer here
https://tylermcginnis.com/react-router-programmatically-navigate/
Had to use withRouter since my header was not rendered by a React Router.
Now, what if the Register component wasn’t being rendered by React Router? (Meaning, we’re not passing Register as a component prop to a Route. Instead, we’re just rendering it ourselves like <Register />). If it’s not rendered by React Router, then we won’t have access to history.push. The team thought of this use case so React Router comes with a handy HOC called withRouter. Going back to our Register code above, by adding withRouter, it would look like this
import {
withRouter
} from 'react-router-dom'
class Register extends React.Component {
handleSubmit = (user) => {
saveUser(user).then(() =>
this.props.history.push('/dashboard')
))
}
render() {
return (
<div>
<h1>Register</h1>
<Form onSubmit={this.handleSubmit} />
</div>
)
}
}
export default withRouter(Register)

Render Component By Clicking Text Passed as Props

I am new to React and am trying to render a component when a user clicks on some text. The text is a state that's updated in a parent component using a form input and the useState React hook. I can find articles on rendering 'onClick' events, but this isn's a form with type submit or a button, it's just text that's passed down to it as a prop from a state higher up. Also a lot of tutorials seem to be outdated. What I would like to do is be able to click on the text and have it render a new component, with the effect of taking the user to a new 'page' in the browser. The rendered text I want to be able to click on is {user.contacts.username} in the code below. Any help much appreciated as I'm not sure which direction to go in.
import './Contacts.css';
import CreateTimer from './CreateTimer';
function Contacts(user) {
return (
<div className="Contacts">
{user.contacts.username}
</div>
);
}
export default Contacts;

Resources