I am using react-route-dom, I want to implement this config:
<Route exact path='/test/:id' component={MyComponent}/>
<Route exact path='/test2/:id' component={MyComponent}/>
then in MyComponent, I want to make something different depending on which route is calling it.
const MyComponent = ({info: {param}, info2: {param1, param2}}) => {
return (
<div>
</div>
)
}
MyComponent.propTypes = {
info: PropTypes.object.isRequired,
info2: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
info: state.info,
info2: state.info2
});
export default connect(mapStateToProps, {})(MyComponent);
How can I get the test or test2 params?
You can get access to the entire path using props.location
You can send it via props:
<Route exact path='/test/:id'>
<MyComponent route={'test'} />
</Route>
Related
I'm converting a React/JavaScript project into a React/TypeScript. I'm using react-router-dom to handle all of the routing stuff. So, I have a file called routes/index.js (Yes, a JavaScript file) and inside of this file you will find something like this:
import { lazy } from 'react'
// use lazy for better code splitting, a.k.a. load faster
const Dashboard = lazy(() => import('../pages/Dashboard'))
const routes = [
{
path: '/dashboard', // the url
component: Dashboard, // view rendered
},
]
export default routes
In another file, that helpme to load the routes, I have the following code:
<Switch>
{routes.map((route, i) => {
return route.component ? (
<Route
key={i}
exact={true}
path={`/app${route.path}`}
render={(props) => <route.component {...props} />}
/>
) : null;
})}
<Redirect exact from="/app" to="/app/dashboard" />
<Route component={Page404} />
</Switch>;
The problem is the <route.component {...props}/>, TypeScript give me this error:
Type '{ history: History; location: Location; match: match<{}>; staticContext?: StaticContext | undefined; }' has no properties in common with type 'IntrinsicAttributes'.
How I can fix this problem?
As far as I know, you are getting this error because of using a lowercase component name. So, you can use something like this:
const Component = route.component as React.ComponentType;
...
return <Component {...someProps} />
you can also provide some generic types for this React.ComponentType
I have a simple setup to test the use of the useContext hook, when you want to change the context value in child components.
A simple Context is defined in its own file like such:
import React from 'react'
const DataContext = React.createContext({})
export const DataProvider = DataContext.Provider
export default DataContext
Then I wrap my router in a provider in a component that exposes its state to use as a reference for the ContextProvider, as such:
import { DataProvider } from './dataContext.js'
export default function App(props) {
const [data, setData] = useState("Hello!")
const value = { data, setData }
const hist = createBrowserHistory();
return (
<DataProvider value={value}>
<Router history={hist}>
<Switch>
<Route path="/admin" component={Admin} />
<Redirect from="/" to="/admin/services" />
</Switch>
</Router>
</DataProvider>
)
}
Finally I have two Views that I am able to navigate between initially, one of them showcasing the context value, as well as containing a button to change it:
export default function EndpointView(props) {
const { data, setData } = useContext(DataContext)
return (
<div>
<h1>{data}!</h1>
<Button onClick={() => setData(Math.random())}>Update context state</Button>
</div>
)
}
The functionality seems to work, as the showcases text is updated.
The problem is, when I have clicked the button, I can no longer navigate in my navbar, even though the url is changing. Any ideas as to why?
This is showcased in this picture, where the url is corresponding to the top-most item in the side bar, even though we are stuck in the "endpoint view"-component.
Edit:
So the routing works by including a switch in the Admin layout:
const switchRoutes = (
<Switch>
{routes.map((prop, key) => {
if (prop.layout === "/admin") {
return (
<Route
path={prop.layout + prop.path}
component={prop.component}
key={key}
/>
);
}
return null;
})}
<Redirect from="/admin" to="/admin/services" />
</Switch>
);
Where the routes (which we .map) are fetched from another file that looks like this:
const dashboardRoutes = [
{
path: "/services",
name: "Services view",
icon: AccountBalance,
component: ServicesView,
layout: "/admin"
},
{
path: "/endpoint",
name: "Endpoint view",
icon: FlashOn,
component: EndpointView,
layout: "/admin"
}
];
export default dashboardRoutes;
I was able to solve this issue.
I suspect the problem was that updating the state reloaded the root router component which caused some issues.
Instead I moved the DataProvider tag one step down the tree, to wrap the switch in the Admin component.
I have a react component that is trying to pass a function to another component, the problem is that
I'm not being able to define the function, it throws a compiling error
export default function App() {
createActivity() { // here I get an error: missing semicolon
console.log("creating activity");
}
return (
<div className = "App" >
<Route path="/" component={ Header } />
<Route exact path="/" component={ShowSplashWindow} />
<Route path="/createactivitiy" render = {() =>
<CreateActivity createActivity={this.createActivity} />} />
</div>
);
}
What am I missing?
Rafael
The function has to be declared as
const createActivity = () => {
console.log("creando la actividad");
}
And when passed as props, it should not be called with "this"
<CreateActivity createActivity={createActivity} />}
You declared component as functional, but trying to create a class method.
You should either use class component:
export default class App extends React.Component {...
with 2 methods: createActivity and render.
Or declare your function and assign to constant
const createActivity = () => ...
but use it without this:
<CreateActivity createActivity={createActivity} />
i want to pass props along with routercomponent props and access it in a component using react and typescript.
i have a MainComponent which has a ChildComponent. I want to pass props namely isOpen, showdialog and hidedialog props to the ChildComponent.
Below is the code,
function MainComponent() {
render= () => {
return(
<Route
exact
path="/items"
render={routeProps => (
<Layout>
<ChildComponent
isOpen={isOpen}// how to access this
showDialog={showDialog} //how to access this
hideDialog={hideDialog} //how to access this
{...routeProps}
/>
<Layout>
)}
/>
)
}
}
function ChildComponent({ history }: RouteComponentProps) {
//some logic
}
I have tried accessing like below
interface Props {
isOpen: boolean;
showDialog: any;
hideDialog: any;
}
function ChildComponent({ history }: RouteComponentProps) {
//how to access it here...
}
I am not knowing how to access it here. could someone help me fix this. thanks.
You can define the props interface to be a combination of RouteComponentProps and ChildProps. Post that destructure them from props
interface Props {
isOpen: boolean;
showDialog: any;
hideDialog: any;
}
function ChildComponent({ history, isOpen, showDialog, hideDialog }: Props & RouteComponentProps<{}>) {
}
P.S on a sidenote , a functional component doesn't have a render function so you just needed to write your MainComponent like
function MainComponent() {
return(
<Route
exact
path="/items"
render={routeProps => (
<Layout>
<ChildComponent
isOpen={isOpen}
showDialog={showDialog}
hideDialog={hideDialog}
{...routeProps}
/>
<Layout>
)}
/>
)
}
Just continuing destructuring it. Just make sure your types are lined up.
function ChildComponent({ history, isOpen, showDialog, hideDialog }: RouteComponentProps) {
//how to access it here...
}
Remember that object you're passing into the function is an instance of React Props, so everything that is a property on the JSX is passed in the props object under the same keys.
I'm trying to test a connected react component that needs a props.params.id to call action creators. When I try to test that the component is connected to the store I get "Uncaught TypeError: Cannot read property 'id' of undefined"
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={PostsIndex}/>
<Route path="posts/:id" component={ShowPost}/>
</Route>
</Router>
</Provider>
, document.querySelector('.container'));
describe('ConnectedShowPost', ()=> {
let initialState = {
posts: { postsList: postsListData, post: postData, error: '' },
comments: {showComments: false},
auth: {authenticated: true}
};
store = mockStore(initialState);
connectedShowPost = TestUtils.renderIntoDocument(<Provider store={store}><ConnectedShowPost/></Provider>);
showPost = TestUtils.scryRenderedComponentsWithType(connectedShowPost, ShowPost)[0];
expect(showPost.props.posts.post).toEqual(postData);
})
I've tried including params in the store but that doesn't work since params is not hooked up to the store when used inside the component.
I've also tried passing it in as an ownProps argument and that didn't work either.
Passing it in to the ConnectedShowPost component as a props causes all other state items in the store to be undefined..
Also tried to directly set showPost.props.params = {id: '123} which didnt work either..
Any ideas on how to get this test to work?
If your connected component receives a param like this:
function mapStateToProps(state, ownProps)
{
const { match: { params } } = ownProps;
console.log(params.id);
return {
......
};
}
Then, In your test it's possible to inject that param as traditional props. Check out the next example:
wrapper = mount(
<MemoryRouter>
<ConnectedContainer
store={store}
match={
{
params: {
id: 1
}
}
}
/>
</MemoryRouter>
);