Use urlParams in mapStateToProps in react-router v6 - javascript

My mapStateToProps needs the component urlParams to select a portion of the redux store and I have tried using but I keep getting the error: "TypeError: ownProps.useParams is not a function"
my code

If I'm understanding your question/code correctly, you are trying to use the collectionId route match param as a key into the selectCollection selector.
In react-router-dom v6 there are no longer route props so rendered components need to use the React hooks to access navigate, location, and match/params. You could go the route of creating a custom withRouter HOC to inject the match params as a prop, and be accessible for the connect HOC, but use of two Higher Order Components is completely unnecessary.
I propose using the hooks in the component.
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
const CollectionPage = () => {
const { collectionId } = useParams();
const collection = useSelector(selectCollection(collectionId));
return (
... JSX ...
);
};
export default CollectionPage;

Related

React and typescript ReactRouterProps issue

I need to deconstruct my general props and match (to get an "id" from the URL).
Component (using props):
interface Props extends RouteComponentProps<{id: string}>{
initialProjectName: string;
workspaceId: string;
}
const AddResources: React.FC<Props> = ({
initialProjectName,
workspaceId,
match,
}) => {
const projectId = match.params.id; // used here without any error
But the parent component is showing error when I pass props
Parent
<div>
<h1>Start Project Page</h1>
<AddResources
initialProjectName={initialProjectName}
workspaceId={workspaceId} // error
/>
</div>
Error Message
By using RouteComponentProps you specify that your component requires the route props, but you will also need to make sure you pass these props. If the component is directly underneath a Route (i.e. as a child or by using <Route component={..}/>, the route props are passed automatically, if not, you can use withRouter (docs) to obtain them:
const AddResourcesWithRouter = withRouter(AddResources);
and use AddResourcesWithRouter istead of AddResources.
I think you should try useHistory, useRouteMatch, useLocation or wrap withRouter(AddResources)

Why can't I consume MyContext in App Component?

import React, { useContext } from "react";
import { ContextProvider, MyContext } from "./Context/MyContext";
import "./styles.css";
export default function App() {
const value = useContext(MyContext);
console.log(value);
return (
<ContextProvider>
<div className="App">{value}</div>
</ContextProvider>
);
}
If I want to consume the useContext exactly on App Component. I get the value as undefined on App Component but the value of all other component is okay. So, I want to know what is the mechanism behind it. Why I can't access to useContext in App component.
The ContextProvider provides the values within your Context to all it's child components. The value you are supplying to your <div> comes from the parent component of ContextProvider in your case, ie the App. At this point your Provider has still not been set up, you have to wrap App inside provider to access the values of the context. In other words the Provider needs to be the Parent of app to provide it values and not the other way around

Pass react-redux store and dispatch functions via props?

The following React component is given:
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { store, StoreState } from "../../redux/actions";
import { setBackgroundAction } from "../../redux/title.actions";
import "./Loader.scss";
interface ReduxProps {
bgClass: string;
}
interface Props extends ReduxProps {
bgChange?: boolean;
}
export default function Loader(props: Props) {
const [bgClassOld, setBgClassOld] = useState<string>("");
const dispatch = useDispatch();
useEffect(() => {
const { bgChange, bgClass } = props;
if (bgChange) {
setBgClassOld(bgClass);
dispatch(setBackgroundAction("bg-white"));
dispatch(setBackgroundAction(bgClassOld));
}
});
return (
<div className="d-flex">
<div className="loader">
<img src="/loadscreen.gif" />
</div>
</div>
);
}
// function mapping(state: StoreState): ReduxProps {
// return {
// bgClass: state.title.backgroundClass,
// };
// }
This is more a theoretical question to see how to actually do the following change:
The component Loader will be imported from another npm package (shared components).
My problem is that I have a redux state in the current implementation included (changed it from Class to Functional component, so thats mapping() is still in there).
As I only import the component in my "main" client, I will not have the whole redux setup in place. So I think I need to pass the store and the dispatch functions via props.
So should I create a prop store for my component, where I pass the redux store when I import the shared component?
Do I also create two props for each dispatch functions?
Does is make sense or would there be a better approach?
You generally shouldn't import the Redux store directly into components. The hooks allow your component to access whatever Redux store has been injected into the component tree by a <Provider>.
You also don't need to pass dispatch as a prop. Any component can call useDispatch(), and dispatch actions to whatever Redux store is actually being used.
If I understand your question, you're planning on importing this component into an existing app, and it sounds like that app is already configured to use (React-)Redux with a <Provider> at the top. If that's the case, then you don't have to do anything else special to make this work. Just call the React-Redux hooks in any of your components.

What is the right way to use new React hook useContext?

I have some difficulties to understand the new way to use react Context API.
I have an app with a custom class Firebase. Now I want to make a hook to pass it. Before I used HOC (higher-order Component) and context.
My questions
Do I need to use HOC or it's a new way to do this?
Do I need the Context.Provider or it's new Hook?
Do I need to declare default value as a null or I can pass my Object
right from context.js
How can I use a new Hook instead of HOC in mine code?
Here is my code with some comments related to questions
// context.js this is my hoc
// index.jsx
import App from './App'
import Firebase, { FirebaseContext } from './components/Firebase'
const FirebaseContext = React.createContext(null)
export const withFirebase = Component => (props) => {
// I don't need to wrap it to the FirebaseContext.Consumer
// 1 But do I need this HOC or it's a new way?
const firebase = useContext(FirebaseContext)
return <Component {...props} firebase={firebase} />
}
ReactDOM.render(
// 2 Here I'm lost. Do I need the FirebaseContext.Provider or not?
// 3 Do I need to declare value her or I should do it in context.js as a default?
<FirebaseContext.Provider value={new Firebase()}>
<App />
</FirebaseContext.Provider>,
document.getElementById('root'),
)
// App.jsx
// 4 Can I use a new Hook instead of HOC here and how?
import { withFirebase } from './components/Firebase/context'
const App = () => {
const firebase = this.props.firebase // But should be useContext(FirebaseContext) or something like this?
return(...)
}
export default withFirebase(App) // I don't need this with the Hook
Any help appreciated.
You should understand it first that, useContext is just to make use of Context and acts like a consumer and not Provider.
To answer your questions
Do I need to use HOC or it's a new way to do this?
You don't need an HOC with hooks. Hooks are meant to replace HOCs and render props pattern.
Do I need the Context.Provider or it's new Hook?
There is no hooks equivalent of Context.Provider. You have to use it as is.
Do I need to declare default value as a null or I can pass my Object
right from context.js
The default value to createContext is only used if you don't pass a value props to the Context.Provider. If you pass it the default value is ignored.
How can I use a new Hook instead of HOC in mine code?
Instead of using useContext in the component returned by HOC use it directly within the component
Sample code
/ context.js this is my hoc
// index.jsx
import App from './App'
import Firebase, { FirebaseContext } from './components/Firebase'
const FirebaseContext = React.createContext(null)
ReactDOM.render(
<FirebaseContext.Provider value={new Firebase()}>
<App />
</FirebaseContext.Provider>,
document.getElementById('root'),
)
App.jsx
const App = () => {
const firebase = useContext(FirebaseContext)
return(...)
}
export default App;
Do I need to use HOC or it's a new way to do this?
No, you don't need to use HOC as best technique.
Why?
Starting from React v7.0, you can use functional-based components.
From this version efficient is to use the the latest
technique named HOOKS, which were designed to replace class and
provide another great alternative to compose behavior into your
components.
Do I need the Context.Provider or it's new Hook?
Hook like useContext() has a relation with Context.Provider.
Context is designed to share data that can be considered “global”.
The Provider component accepts a
value prop to be passed. Every Context come with a Provider.
Context.Provider component available on the context instance is used to provide the context to its child components, no matter how deep they are.
Do I need to declare default value as a null or I can pass my Object right from context.js?
No, you don't need necessarily to declare a default value.
Example of defining the context in one corner of the codebase without defaultValue.
const CountStateContext = React.createContext() // <-- define the context without defaultValue
How can I use a new Hook instead of HOC in mine code?
index.jsx
import App from './App'
import Firebase, { FirebaseContext } from './components/Firebase'
const FirebaseContext = React.createContext(null)
ReactDOM.render(
<FirebaseContext.Provider value={new Firebase()}>
<App />
</FirebaseContext.Provider>,
document.getElementById('root'),
)
Root Component: App.js, where will be used data comes form context:
const App = () => {
const firebase = useContext(FirebaseContext)
return(...)
}
export default App;

Using browserHistory.push to change Route dynamically doesn't work with react-router v4

I try to do a simple redirect in a function...
I tried this:
Router.browserHistory.push('/dashboard');
But then I got this error:
Uncaught TypeError: Cannot read property 'push' of undefined
whats my fail?
Creating a new browserHistory won't work because <BrowserRouter> creates its own history instance, and listens for changes on that. So a different instance will change the url but not update the <BrowserRouter>.
browserHistory is not available form react-router-dom package from v4 , and is separated to history package.
Navigating with WithRouter
You can rather make use of withRouter Higher Order Component and navigate with history prop
From the official documentation
You can get access to the history object’s properties and the closest <Route>'s match via the withRouter higher-order component. withRouter will re-render its component every time the route changes with the same props as <Route> render props: { match, location, history }.
Sample snippet
import {withRouter} from "react-router";
class MyComponent extends React.Component {
...
changeRoute = () => {
this.props.history.push('/dashboard)
}
...
}
export default withRouter(MyComponent);
Also see this answer for more info on nesting and dynamically routing in react-router-v4
Nesting routes and dynamically routing in React-router v4
As mentioned in the comments you should use this new approach for v4 react router but if you are looking for a quick workaround you can use context.
```javascript
import { PropTypes } from 'prop-types'
import React from 'react'
export default class MyComponent extends React.Component {
...
// redirect to dashboard
redirectToDashboard = () => {
this.context.router.history.push('/dashboard');
}
}
MyComponent.contextTypes = {
router: PropTypes.object
}
This should do the trick

Categories