I am writing an react website that has a master-detail feel to it. I make server calls to populate my master component. When a user clicks an item on the master component, it uses React Router to navigate to a detail view. However, my state is no longer populated. Instead it appears I'm getting my initial state in this new component.
As far as I know, I'm using the latest on all the libraries.
index.js
const store = storeFactory( initialState );
const myHistory = createBrowserHistory();
ReactDOM.render(<Provider store={store}>
<Router history={ myHistory }>
<div>
<Route exact path='/' component={App} />
<Route exact path='/contrib' component={Contributions} />
<Route exact path='/story/:id' component={FullStory} />
</div>
</Router>
</Provider>,
document.getElementById('root'));
Story.jsx (a child of App)
<div className="faux-row">
<Link className="invisible-link" to={`/story/${this.props.story.id}`}>
<div className="story">
<div className="story-header">
FullStory.jsx
const mapStateToProps = (state) => {
console.log(state);
return {
stories: state.story_groups
}
}
story_groups as shown here is empty. How do I keep the state?
EDIT:
I am using redux-sage for middleware to call the server. The dispatch is coming out of ComponentWillMount() in the App component.
Related
I have a fucntion that I want it to replace the components between Header and Footer components.
Where/How should I use the defaultRouter function with the history.push. I see only the URL is changed.
The the Header component is updating the pageIndex.
I tried to change defaultRouter to a useCallback function but that didn't worked at all.
What i the order of theses functions?
App.js
function App() {
const [searchResults, setSearchResults] = useState([]);
const [itemsAmount, setItemsAmount] = useState('');
const [pageIndex, setPageIndex] = useState();
const history = createBrowserHistory();
// let history = useHistory();
const defaultRouter = useMemo(() => {
console.log(pageIndex);
switch (pageIndex) {
case 1:
history.push({ pathname: '/search', state: searchResults });
break;
case 2:
history.push('/cart');
break;
default:
history.push('/');
break;
}
}, [pageIndex]);
return (
<div className='App'>
<Header
settingResults={setSearchResults}
itemsCartAmount={itemsAmount}
setPageIndex={setPageIndex}
/>
{defaultRouter}
<Footer />
<Router history={history}>
<Switch>
<Route exact path='/'>
<Home />
</Route>
<Route path='/search'>
<Search/>
</Route>
<Route path='/cart'>
<Cart />
</Route>
<Route component={PageNotFound} />;
</Switch>
</Router>
</div>
);
}
Edit
I just removed BrowserRouter from Index.js, the same problem.
Index.js
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
This way above the pageIndex is updating but history.push don't switch the components.
What I think is happening here is that history object is getting created everytime when the pageIndex is being updated. Ideally it should be created just once manually.
You can find the working solution here Sandbox.
I have updated
the logic for creating history object
removed Router from App
used useHistory to get the history object in the component.
also wrote the effect to push changes when pageIndex is updated.
update
please have a look at this, I tried to replicate your issue.
https://codesandbox.io/s/amazing-swartz-jxc5p?file=/src/App.js
You have used Router twice, one inside App.js as <Router> and other inside index.js as <BrowserRouter>.
What you need to do is; only use inside App.js and make sure it is imported as:
import {Router} from 'react-router-dom';
as Router accepts history prop, not the BrowserRouter, and also remove BrowserRouter from index.js.
For safer side, use useHistory() hook for history value.
This will fix your issue.
i'm using react-redux in my project.
I have some component where i'm dispatch some action and get data from reducer into a component on componentWillMount method here is problem when i change to route componentWillMount method not firing my actions or anything else. for that i need to refresh page.
here is my componentWillMount Method:
componentWillMount() {
console.log("d")
this
.props
.getChartData()
data = this.props.chartData
}
and here is my main.js where i import my components and assign routing.
import CompA from 'components/CompA'
import CompB from 'components/CompB'
const App = () => (
<Container fluid>
<div>
<main>
<Switch>
<Route exact path="/page-a" component={ComponentA} />
<Route exact path="/page-b" component={ComponentB} />
</Switch>
</main>
</div>
</Container>
)
export default App
where i mistake or what i need to do with it?
and its index.js:
const target = document.querySelector('#root')
console.log(store.getState())
render(
<Provider store={store}>
<ConnectedRouter history={history}>
<div className="row">
<App />
</div>
</ConnectedRouter>
</Provider>,
target
)
if you use redux you should use mapStateToProps to change the state in your component
I think you are using ConnectedRouter from the package react-router-redux, which is now deprecated.
For react-router 4, You need to use withRouter react-router-dom to wrap the component before connecting it like this:
withRouter(connect(...)(MyComponent))
You can get also get the match, location, history properties via props once the component is wrapped in withRouter.
For further information on withRouter, you can visit the following link: https://reacttraining.com/react-router/web/api/withRouter
I've seen problems like this but neither of them solved my problem.
I have a p tag that onClick is supposed to render a page.
But when I click on it and use this.props.history.push(/posts/${id}) It changes the url but doesn't render the component I want.
I have tried using a
onClick={(e, id) => this.postPage(e, post.id)}
to trigger a function that will trigger the component I want to show up. I also tried using
<Link to-=''>
insted of
<p onClick>
but it doesn't work either.
This is what I have done so far, this is the function that triggers when I click the p tag:
postPage = (e, id) => {
const { history } = this.props;
history.push(`/post/${id.toString()}`);
}
and also, I used withRouter to have access to the history prop.
export default withRouter(connect(null, mapDispatchToProps)(Post));
And these are the routes I mapped:
render() {
return (
<div className="Routes">
<Switch>
<Route exact path='/' component={Home}></Route>
<Route path='/post/:id' render={(history) => (
<PostPage {...history}></PostPage>
)}></Route>
</Switch>
</div>
);
}
}
And of course, when I call this component I also pass using BrowserRouter
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<Routes />
</BrowserRouter>
</Provider>
, document.getElementById('root'));
I just wanted to render the component but only the URL changes and nothing happens.
A good approach would be to implement react-redux and react-router-dom one at a time, to ensure that there are no clashes. The double HOC here:
export default withRouter(connect(null, mapDispatchToProps)(Post));
Is worth breaking down to:
export default withRouter(Post);
To ensure that react-router-dom works as expected, then implementing react-redux once routing is in place.
For those who have had the same problem the solution was to change the BrowserRouter tag location from the index.js to inside of the component that uses the Route. I was doing something like:
ReactDOM.render(<Provider>
<BrowserRouter>
<App>
</BrowserRouter>
</Provider>, document.getElementById('root'));
and now inside the Routes.js component
render() {
<BrowserRouter>
<Route ...>
<Route ...>
</BrowserRouter>
}
I'm beginner in react, I'm using React 4, Redux and react-router-dom package for my router.
I have an App.js file which connects Main with mapStateToProps & mapDispatchToProps
//App.js
const App = connect(mapStateToProps, mapDispatchToProps)(Main);
My Main component renders a header and clones child elements
//Main.js
export default class Main extends React.Component {
render() {
return (
<div>
<h1>Hi I'm Header</h1>
{React.cloneElement(this.props.children, this.props)}
</div>
);
}
}
My child components contains a simple Json stringify of props
//UserList.js
export default class UsersList extends React.Component {
render(){
return (
<pre>
{JSON.stringify(this.props, null, "")}
</pre>
);
}
}
I made an index.js file which has a route and contains provider, and provider child is a router and my router contains other app routes
//index.js
const router = (
<Provider store={store}>
<App>
<Router history={history}>
<Switch>
<Route exact path="/" component={UsersList}/>
</Switch>
</Router>
</App>
</Provider>
);
render(router, document.getElementById('root'));
As you see I have an App, my app connects main to mapstatetoprops and mapdispatchtoprops, and finally gives the child route (UsersList component) to the parent (Main Component) and in the end it renders the child.
When I stringify the child component props it show only history, location and other props of route, but it's not showing my Users props which is the default state of the app!
And when I use react developer tools it shows there is other router inside my router!
Duplicated Router Screenshot
I found out that when I put the router in something (component, element or whatever like ) it creates another router inside itself and the child router doesn't have the parent props.
I also tried to add
{...this.props}
to routes or
render={(props) => <UserList {props} />}
and also another way that I tried was using nested routes like
const router = (
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<Route exact component={UsersList}/>
<Route path="/single/:id" component={SingleUser}/>
</Route>
</Router>
</Provider>
);
but the result was this error:
React.cloneElement(...): The argument must be a React element, but you passed undefined.
Thanks.
Since you want to pass the props from App component to UsersList, you need to restructure your Routes differently since with the current way, the props are passed on to the Router, but Router doesn't pass them down to the Route component
index.js
const router = (
<Provider store={store}>
<Router history={history}>
<Route component={App} />
</Router>
</Provider>
);
render(router, document.getElementById('root'));
Main.js
export default class Main extends React.Component {
render() {
return (
<div>
<h1>Hi I'm Header</h1>
<Switch>
<Route exact path="/" render={(routerProps) => <UsersList {...this.props} {...routerProps}/>}/>
</Switch>
</div>
);
}
}
I'm trying to build a React app/site that uses "react-router", "redux" and server-side rendering and I don't quite understand how to deal with the initial rendering in the browser.
On the server side, I can take a JSON object, pass it to the state and then map state to props - all synchronously - and finally render the component using already initialized props.
const routes = <Router>
<Route path='/' component={ViewComposite}
onEnter={ ({params}) => { store.dispatch(setViewProps(props)); }} />
<Route path='/admin' component={AdminComposite}
onEnter={ ({params}) => { store.dispatch(setAdminProps(props)); }} />
</Router>;
However, when the browser comes into play, I need to fetch data via AJAX request - asynchronously:
window.onload = function() {
const store = createStore(viewPropsReducer);
ReactDOM.render(
<Provider store={store} >
<Router history={browserHistory} >
<Route path="/" component={ViewComposite}
onEnter={ ({params}) => {store.dispatch(fetchViewData()); }} />
<Route path='/admin' component={AdminComposite}
onEnter={ ({params}) => {store.dispatch(fetchAdminData()); }} />
</Router>
</Provider>,
document.getElementById('viewMount')
);}
As a result, the rendering fails due to undefined props.
Is there a way to work around this - maybe postpone browser rendering until the data is fetched?
You could add the json payload to the server rendered html.
<script>
window.payload = <json>
</script>
If your state is serializable, I guess you could just put the state there instead, and pass it as initialState when creating the store.
createStore(reducer, window.initialState);