React router memoized route wizard - javascript

I have a wizard component that is built around a class holding the questions in an array and passing the current question to a Wizard component.
I wish for each question to have a corresponding route.
The question object inside the array:
[{
route: 'service-option',
getComponent: props => {
return <QuestionOne {...props}/>;
},
...
},
...
]
I'm trying to render a memoized react Route component based on the question prop passed to the Wizard like so:
const memoizedQuestionRoute = useMemo(() => {
history.push(`wizard/${question.route}`);
const component = React.cloneElement(question.getComponent(...), {...});
return <Route path={`wizard/${question.route}`} render={props => component}/>;
}, [question]);
and render it in a div like so:
<div>
{memoizedQuestionRoute}
</div>
Problem is that the route is not even rendering and I don't see it inside the div. Any ideas why and how to solve this?

if you haven't decided yet. Get this in component.
import { __RouterContext as RouterContext } from 'react-router-dom';
import { useContext } from 'react';
/**
* useRouter hook for react-router
*/
const useRouter = () => useContext(RouterContext);
export default useRouter;

Related

Unable to get properties off navlink [duplicate]

This question already has answers here:
How to pass data from a page to another page using react router
(5 answers)
Closed 4 months ago.
I'm teaching myself React (using v18.1.0) and I'm struggling to understand why I am getting an undefined object off the properties I'm passing to a component through a NavLink using react-router-dom v.6.3.0.
I have a component that has a NavLink that is created when a certain variable (let's call it "var1") is not null, like so:
[...]
{
var1 != null
?
<NavLink className="Menu-Button" to={{pathname : "/Component1"}} state = {{ props : var1 }}>ButtonContent</NavLink>
: <p/>
}
[...]
The component being routed to (let's call it "Component1") looks like this:
import React from 'react';
import {useLocation} from 'react-router-dom';
const Component1= () => {
const location = useLocation();
const { props } = location;
console.log(props);
return(
[...]
)
};
export default Component1;
The output of that console.log is undefined. I've also tried using props.location instead of useLocation(), but I get the same issue. Where am I going wrong?
EDIT:
Including route config in App.js as requested by #Nick Vu:
N.B. Toolbar is a component that acts as a header / navigator
[all the imports]
const App = () => {
return (
<BrowserRouter>
<Toolbar />
<Routes>
[all the existing routes that work fine]
<Route exact path='/Component1' element={<Component1 /> } />
</Routes>
</BrowserRouter>
);
}
export default App;
Your NavLink seems good, but according to your setup, in Component1, you should access props values from NavLink by state.
import React from 'react';
import {useLocation} from 'react-router-dom';
const Component1= () => {
const location = useLocation();
const props = location?.state?.props; //use `?` to avoid errors from missing states
console.log(props)
return(
[...]
)
};
export default Component1;
Sandbox link
This was embarassing but, changing
state = {{ props : var1 }}
to
state={{props : var1}}
has resolved the issue.

REACT ROUTER - Class component (only) - rendering page - JSON File

i'm working on react router project (im beginner) to improve my skillz.
my problem:
I have a JSON file (Dragon ball Z). When i click on characters (like goku) i want to show his biography.
Actually when i click on goku every biography of each characters are showed.
I know how to do it with function component (useLocation ect..) but i'm totally stuck wicth class component because i can't use hooks in it, what is the good way to do it ?
here is the project :
DBZ REACT ROUTES
Thanks
Using react-router-dom v6 we can use useParams to read the params key within function component.
With class component you can write an HOC function to archive the same thing
a higher-order component is a function that takes a component and returns a new component
import { useParams } from 'react-router-dom'
function withParams(Component) {
return props => <Component {...props} params={useParams()} />;
}
class Charbio extends React.Component {
componentDidMount() {
let { id } = this.props.params;
// get the bio by id here
}
render() {
return API.map((element) => {
return <p>{element.bio}</p>;
});
}
}
export default withParams(Charbio);

Custom hook's state does not update across all components?

import { useState } from 'react';
export default function usePrivacyMode() {
const [isPrivacyOn, setIsPrivacyOn] = useState(false);
return {
isPrivacyOn,
setIsPrivacyOn
};
}
This is my custom hook. I set the state in PrivacyIcons component, and then I use isPrivacyOn for show/hide values from a table based on the value. But in a different component the isPrivacyOn is not changed, it's changed only in PrivacyIcons? Why I can't change it in one component and then use the value across all components? Thanks.
states are not meant to be shared across components. You are looking for useContext. This allows you to share a function and a state between components. React has an excellent tutorial on how to do it in the official documentation: https://reactjs.org/docs/hooks-reference.html#usecontext
For your specific example it would look something like this:
Your App.js
import { useState } from 'react';
export const PrivacyContext = createContext([]);
const App = (props) => {
const [isPrivacyOn, setIsPrivacyOn] = useState(false);
return (
<PrivacyContext.Provider value={[isPrivacyOn, setIsPrivacyOn]}>
<ComponentUsingPrivacyContext />
{props.children}
</PrivacyContext.Provider>
);
};
export default App;
Keep in mind that any component that wants access to that context must be a child of PrivacyContext
Any component that wants to use PrivacyContext:
import React, { useContext } from "react";
import {PrivacyContext} from "...your route";
const ComponentUsingPrivacyContext = (props) => {
const [isPrivacyOn, setIsPrivacyOn] = useContext(PageContext);
return (
<button onclick={setIsPrivacyOn}>
Turn Privacy On
</button>
<span>Privacy is: {isPrivacyOn}</span>
);
};
export default ComponentUsingPrivacyContext;

Context API, how to not use a class?

How to make and get props in all components with the funcional component and hooks?
at example iam making a js file with class with functions inside like getBooks(), getNotes(), then i making a context file and importing this in index.js and use a class for provider value, like below.
import {BookStoreContext} from "./components/bookstore-service-context";
import {BookStoreService} from "./services";
const bookStoreService = new BookStoreService();
ReactDOM.render(
<Provider store={store}>
<ErrorBoundry>
<BookStoreContext.Provider value={BookStoreService}>
<BrowserRouter>
<App/>
</BrowserRouter>
</BookStoreContext.Provider>
</ErrorBoundry>
</Provider>,
document.getElementById('root')
);
Once our Context is ready ( A global data to be used in the project).We can use useContext() Hook to consume our context in React-Hooks. This way we avoid class Based ContextName.Consumer Component in our Application.
The will provide step by step guide about how to work with Context API in Hooks with the example below.
1. First we create our Context File (Which will have our Global Data)
import React, { useState, createContext } from "react";
const books = [
{ id: 1, name: "The way of the kings" },
{ id: 2, name: "The people of Paradise" },
{ id: 3, name: "Protest of the Farmers" }
];
// First we need to create our context
const BookContext = createContext();
// createContext returns 2 things. Provider and Consumer. We will only need Provider
const BookContextProvider = ({ children }) => {
const [books, setBooks] = useState(books);
const removeBook = (id) => {
setBooks(books.filter((book) => book.id !== id));
};
return (
<BookContext.Provider value={{ books, removeBook }}>
{children}
</BookContext.Provider>
);
};
export default BookContextProvider;
We used createContext() method to create our Context. This returns (as before Hooks also) 2 things. Consumer and Provider. Since we will work with Hooks we only need Provider and in place of Consumer, we'll use useContext() in our components to consume this context.
2. Our Context is ready now. (Note:BookContext is our Context and BookContextProvider is simply a component in which we have our Context data). We will need to wrap our entire App aroundBookContextProvder Component so that all the Child Components used in the Application will have access to the Global Context.
import React, { createContext, useState, useEffect } from "react";
import "./styles.css";
import BookContextProvider from "./BookContext";
export default function App() {
return (
<div className="App">
<BookContextProvider>
<BookList/>
</BookContextProvider>
</div>
);
}
If you notice, I have Used BookList Component within BookContextProvider, that is to do with the setup we did in our Context file, where we used {children}. So, BookList Component is passed as children prop to the BookContextProvider Component in our BookContext.js file. (This may take some time for newbies to grasp the concept).
3. Once All the setup is ready we can consume context in our Child Components:
So in my BookList Component, I want to access books and also have the access to the removeBook handler. *We make use of useContext() Hook to do that.*
import React, { useContext } from "react";
import { BookContext } from "./BookContext";
const BookList = () => {
const { books, removeBook } = useContext(BookContext);
console.log(books); // We have our Books available now
console.log(removeBook); // We have our removeBook Handler as well
return (
<div>
<h1>BookList Component</h1>
{books.length > 0 &&
books.map((book) => {
return <div key={book.id}>{book.name}</div>;
})}
</div>
);
};
export default BookList;
In the above BookList Component we are now consuming our Context
using useContext() Hook.
COMPLETE CODESANDBOX DEMO: https://codesandbox.io/s/react-context-and-hooks-vcsfn?file=/src/App.js
See useContext.
Just do
// functional component
const someComponent = () => {
const BookStoreService = useContext(BookStoreContext);
// calling a method in the BookStoreService class
BookStoreService.getBooks();
return <></>
}
Note that if your methods like getBooks() are asynchronous (i.e. fetch data from a server), it's probably best to call them within a side effect hook like useEffect.

Cannot update a component (`App`) error in component

I have the following component:
import React, { useState, useEffect, useContext } from 'react';
import PropTypes from 'prop-types';
import { Redirect } from 'react-router-dom';
import DashboardContext from '../../contexts/DashboardContext';
import authorizeWorker from '../../workers/authorize-worker';
/**
* A protected route that calls the authorize API and redirects to login
* on fail
* #param {Function} component The component to redirect to on success
*/
const ProtectedRoute = ({ component }) => {
const [isAuthenticated, setIsAuthenticated] = useState(null);
const dashboardContext = useContext(DashboardContext);
dashboardContext.setIsDashboard(true);
const Component = component;
useEffect(() => {
authorizeWorker({})
.then(() => setIsAuthenticated(true))
.catch(() => setIsAuthenticated(false));
}, []);
if (isAuthenticated === true) {
return <Component />;
}
if (isAuthenticated === false) {
return <Redirect to="/login" />;
}
return null;
}
ProtectedRoute.propTypes = {
component: PropTypes.func
};
export default ProtectedRoute;
I use this for my router e.g.
<ProtectedRoute path="/projects" component={Projects} />
I am recently seeing a warning in the console: Warning: Cannot update a component (App) while rendering a different component (ProtectedRoute). To locate the bad setState() call insideProtectedRoute, follow the stack trace as described. Why am I seeing this error and how can I fix it?
The error is because on initial render phase, you render the component with setIsDashboard(true);, usually, you want to do it on mount (useEffect with empty dep array).
There is an initial render phase, then mount phase, see component's lifecycle diagram.
Be sure that setIsDashboard is persistent, meaning it is created by React API (like useState).
Or its memoized with useMemo/useCallback, or you will get inifite loop because on every render a new instance of setIsDashboard will be created and the dep array ([setIsDashboard]) will cause another execution.
const ProtectedRoute = ({ component }) => {
const { setIsDashboard } = useContext(DashboardContext);
// Make sure `setIsDashboard` persistant
useEffect(() => {
setIsDashboard(true);
}, [setIsDashboard]);
...
};
The error is because you can't set state when you are rendering:
dashboardContext.setIsDashboard(true); is probably the problem.
You don't post your stack trace or line numbers so it's hard to tell exactly what the issue is:
https://stackoverflow.com/help/minimal-reproducible-example

Categories