can't pass object as a prop React - javascript

UPDATED: I added the whole code of the parent component. The idea is to get data from coponent DarbaiLT, store that data in the object named 'objektas'. Then to pass that data as a prop to component AnObject.
import React, { Component } from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import DarbaiLT from "./DarbaiLT";
import AnObject from './AnObject';
const objektas = {
name: 'hello',
}
class App extends Component {
onObjectClick = (darbas) => {
objektas.value = darbas;
console.log('objektas: ', objektas.value);
}
render() {
return (
<Router>
<div>
<Switch>
<Route path="/object" exact component={AnObject} />
<DarbaiLT onObjectClick={this.onObjectClick} />
<AnObject info={objektas}/>
</Switch>
</div>
</Router>
);
}
}
export default App;
I console.log it and it still gives undefined. I also am using Router and Switch, but that shouldn't be any difference right?
import React from "react";
const AnObject =(props)=> {
console.log(props.info);
return (
<>
<div>
<img src='../images/slide4.jpg' width="100%" />
</div>
</>
);
}
export default AnObject;

Related

How to render a react component on landing?

I am new to react app trying to add a component and loading when app opens but it is showing in console Matched leaf route at location "/" does not have an element. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page what is implemented wrong please help
App.js
import React from 'react';
import './app.css';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import JOB_DESC from './job_desc';
function App() {
return (
<Router>
<Routes>
<Route exact path='/' component={JOB_DESC} />
</Routes>
</Router>
)
}
export default App;
job_desc.js
import React, { Component } from 'react';
import './app.css';
import ReactImage from './react.png';
export default class JOB_DESC extends Component {
state = { data: null };
componentDidMount() {
fetch('/api/getUsername')
.then(res => res.json())
.then(res => this.setState({ data : res.data}));
}
render() {
const { data } = this.state;
console.log("DATA", data);
return (
<div>
<h1>Test</h1>
{username ? <h1>{`Hello ${username}`}</h1> : <h1>Loading.. please wait!</h1>}
<img src={ReactImage} alt="react" />
</div>
);
}
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
Try adding <JOB_DESC /> as component in the Route

Can't access url params, passed using react-router 4

I'm having issues accessing a parameter called bookId from the Reader.js component. The parameter is passed down from BookCarouselItem.js using react-router. Reader.js is a connected component.
I'm not sure if that makes a difference, but does react-router work with redux connected components? Or do I need to use something like connected-react-router?
I've tried to refer to similar questions but wasn't able to find a solution, help would be greatly appreciated.
App.js
import React, { Component } from 'react'
import { BrowserRouter as Router, Route } from 'react-router-dom'
import { routes } from 'constants/index';
import Reader from 'components/reader/Reader'
Class App extends Component {
render() {
return (
<div className='container-fluid main-container'>
<Router>
<div>
<Route
path={'/reader/:bookId'}
component={() => <Reader />}
/>
</div>
</Router>
</div>
);
}
}
export default App;
BookCarouselItem.js
import React from 'react'
import { Link } from 'react-router-dom'
export class BookCarouselItem extends React.Component {
render() {
const { bookThumbnail } = this.props;
const { name, numberOfSections } = bookThumbnail;
const bookId = 0;
return (
<Link className='book-carousel-link' to={`/reader/${bookId}`}>
<div className='book-info-overlay'>
<h5>{name}</h5>
<span>{numberOfSections} Sections</span>
</div>
</Link>
);
}
}
export default BookCarouselItem;
Reader.js
import React from 'react'
import { connect } from 'react-redux'
import { compose } from 'recompose'
export class Reader extends React.Component {
render() {
const { match, pageLevel } = this.props;
console.log(match); // undefined
return (
<div>
<div className='reader-body'>
<Book bookId={match.params.bookId}
pageLevel={pageLevel}
bank={bank}/>
</div>
);
}
}
Const mapStateToProps = (state) => {
return {
metadata: state.book.metadata,
pageLevel: state.book.pageLevel
}
};
const authCondition = (authUser) => !!authUser;
export default compose(
withAuthorization(authCondition),
connect(mapStateToProps, mapDispatchToProps),
)(Reader);
You can just give the component to the component prop and the route props will be passed down to the component automatically.
<Route
path="/reader/:bookId"
component={Reader}
/>
If you want to render something that is not just a component, you have to pass down the route props manually.
<Route
path="/reader/:bookId"
render={props => <Reader {...props} />}
/>
I'm not sure but maybe mapStateToProps rewrite you props so could you please first read this issue

history or context .push() not working because of blank object [duplicate]

I have a Dashboard with rotating slides, each of which has a corresponding tab in Bldgs. Both Dashboard.js and Bldgs.js are children to my App.js.
When a user clicks on a specific slide A in Dashboard.js, Dashboard needs to tell App.js so that App can tell Bldgs.js to have tab A displayed when it routes to Bldgs.
I believe that I am passing the correct index value from Dashboard up to App and down to Bldgs. However, an error is being thrown in my App.js file stating:
Uncaught TypeError: Cannot read property 'push' of undefined
My code was working fine before I started passing my handleClick() function to my Dashboard component.
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
import injectTapEventPlugin from 'react-tap-event-plugin';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import { BrowserRouter as Router } from 'react-router-dom';
import { hashHistory } from 'react-router';
// Needed for onTouchTap
// http://stackoverflow.com/a/34015469/988941
injectTapEventPlugin();
ReactDOM.render(
<MuiThemeProvider>
<Router history={hashHistory}>
<App />
</Router>
</MuiThemeProvider>,
document.getElementById('root')
);
App.js
import React from 'react';
import { Route } from 'react-router-dom';
import Dashboard from './Dashboard';
import Bldgs from './Bldgs';
var selectedTab;
class App extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
selectedTab = 0;
}
handleClick(value) {
selectedTab = value;
// console.log(selectedTab);
this.props.history.push('/Bldgs');
// console.log(this.props);
}
render() {
var _this = this;
return (
<div>
<Route exact path="/" render={(props) => <Dashboard {...props} handleClick={_this.handleClick} />} />
<Route path="/Bldgs" component={Bldgs} curTab={selectedTab} />
</div>
);
}
}
export default App;
Dashboard.js
import React, { Component } from 'react';
import './Dashboard.css';
import { AutoRotatingCarousel, Slide } from 'material-auto-rotating-carousel';
...
var curIndex;
class Dashboard extends Component {
constructor(props) {
super(props);
this.handleEnter = this.handleEnter.bind(this);
this.handleChange = this.handleChange.bind(this);
curIndex = 0;
}
handleEnter(e) {
// console.log(curIndex);
this.props.handleClick(curIndex);
}
handleChange(value) {
// console.log(value);
curIndex = value;
}
...
}
export default Dashboard;
Bldgs.js
...
var curTab;
class Bldgs extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.goHome = this.goHome.bind(this);
curTab = 0;
}
handleChange(value) {
this.setState({'selectedTab': value});
console.log(this.state);
}
goHome(e) {
this.props.history.push('/');
}
...
}
export default Bldgs;
In order to make use of history in the App component use it with withRouter. You need to make use of withRouter only when your component is not receiving the Router props,
This may happen in cases when your component is a nested child of a component rendered by the Router or you haven't passed the Router props to it or when the component is not linked to the Router at all and is rendered as a separate component from the Routes.
import React from 'react';
import { Route , withRouter} from 'react-router-dom';
import Dashboard from './Dashboard';
import Bldgs from './Bldgs';
var selectedTab;
class App extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
selectedTab = 0;
}
handleClick(value) {
selectedTab = value;
// console.log(selectedTab);
this.props.history.push('/Bldgs');
// console.log(this.props);
}
render() {
var _this = this;
return (
<div>
<Route exact path="/" render={(props) => <Dashboard {...props} handleClick={_this.handleClick} />} />
<Route path="/Bldgs" component={Bldgs} curTab={selectedTab} />
</div>
);
}
}
export default withRouter(App);
Documentation on withRouter
for React-router V4
change the function to
onClick={this.fun.bind(this)}
fun() {
this.props.history.push("/Home");
}
and
import { withRouter } from 'react-router-dom';
export it later as:
export default withRouter (comp_name);
When working with functional components we can use useHistory() to history to push method
import { useHistory } from "react-router-dom";
and then in function we have to assign the useHistory()
let history = useHistory();
Now We can use history.push to relocate to desired page
history.push('/asdfg')
You are trying to push with out giving the task to a valid library. on App.js
So:
on App.js
You are using BrowserRouter which is handling the history of pages by default, with this react-router-dom function you are relying on BrowserRouter to do this fore you.
Use Router instead of BrowserRouter to gain control of you're history, use history to control the behavior.
Use npm history "yarn add history#4.x.x" / "npm i history#4.x.x"
import Route from 'react-router-dom'; //don't use BrowserRouter
import createBrowserHistory from 'createBrowserHistory';
Remember to exoport it !!
export const history = createBrowserHistory();
4.import history to Dashboard.js
5.import history to Bldgs.js
hope this helps !!!
#brunomiyamotto
See my App.js file below. I ended up passing {...this.props} to the NavBar Component that I had created.
The NavBar component is not part of my Switch routes.
import React, { Component } from 'react';
import { Route, Link, Redirect, Switch, withRouter } from 'react-router-dom'
import Navbar from './Navbar.js';
import Home from './Home/HomeCont';
import Login from './Register/Login';
import Dashboard from './Dashboard/DashboardCont';
import './App.css';
class App extends Component {
constructor(props){
super(props);
}
render() {
return (
<div className="App">
<header className="App-header">
//SOLUTION!!!
<Navbar {...this.props}/>
</header>
<Switch>
<React.Fragment>
<Route exact path="/" render={(props) => <Home {...props}/>}/>
<Route exact path="/login" render={(props) => <Login {...props}/>}/>
</React.Fragment>
</Switch>
</div>
);
}
}
export default withRouter(App);
Within my Navbar. The button I used had the following code. I had to .bind(this) to see the history and be able to user this.props.history.push("/")
<Button
color="inherit"
onClick={this.handleLogout.bind(this)}>Logout</Button>
My mistake was the wrong import in conjuction with BrowserRouter, ie:
incorrect:
import { useHistory } from 'react-router'
correct:
import { useHistory } from 'react-router-dom'
I solved this error by wrapping the component inside the BrowserRouter.
Don't forget about this, it's a very common mistake.
import { BrowserRouter} from 'react-router-dom';
<BrowserRouter>
<Menu/>
<BrowserRouter>
Only the router childrens receive the history hook.

react router - unable to pass store down

I am unable to make the store available to children components.
The setup is a SPA with Symfony as back-end, though this should not make a difference for this matter.
The entry point for Webpack is the file:
/client/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux';
import ReduxPromise from 'redux-promise';
import Root from './App';
import registerServiceWorker from './registerServiceWorker';
import reducers from './pages/combine_reducers';
let composeEnhancers = typeof(window) !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(
reducers,
composeEnhancers(
applyMiddleware(ReduxPromise)
)
)
ReactDOM.render(
<Root store={store} />
, document.querySelector('#root')
);
registerServiceWorker();
The apps as such is at:
/client/App.js
import React from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import {
BrowserRouter as Router,
Route,
Link,
Switch
} from 'react-router-dom';
import HomePage from './pages/home/';
import AccountPage from './pages/account/';
const Root = ({ store }) => {
return(
<Provider store={store}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<Router>
<div>
<Link to="/account">Account</Link>
<Link to="/">Home</Link>
<div>
<Switch>
<Route path="/account" component={AccountPage} />
<Route path="/" component={HomePage} />
</Switch>
</div>
</div>
</Router>
</div>
</Provider>
)
}
Root.propTypes = {
store: PropTypes.object.isRequired
}
export default Root;
So far so good. The store is available in App.js.
But that's not the case at the next level. As you can see I'm attempting to make the store available using connect().
/client/pages/home/index.js
import React from 'react';
import { connect } from 'react-redux';
import Register from '../common/register/';
import PropTypes from 'prop-types';
class Home extends React.Component {
constructor(props){
super(props)
console.log(props);
}
render() {
return (
<div>
<h1> Hello World from home! </h1>
<Register />
</div>
);
}
}
Home.propTypes = {
store: PropTypes.object.isRequired
}
const mapStateToProps = (state) => {
return {
store: state.store,
}
}
export default connect(mapStateToProps)(Home)
At the lower level, the Register component, I'm able to submit the form, but the store not being available, I am unable to capture the response coming from the server.
/client/pages/common/register/index.js
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import RegisterForm from './containers/register';
import { actionSubmitRegister } from './actions/';
import PropTypes from 'prop-types';
class Register extends React.Component{
constructor (props) {
super(props);
this.state = {
registerResponse: '',
}
this.onSubmitRegister = this.onSubmitRegister.bind(this);
}
onSubmitRegister (event) {
event.preventDefault();
let submitForm = new Promise((resolve, reject) => {
actionSubmitRegister(this.props.form.RegisterForm.values);
});
submitForm.then((response) => {
console.log('response',response);
this.setState({registerResponse: this.props.submit_register.data});
console.log('registerResponse', this.state.registerResponse);
}).catch((error) => {
console.log(error);
});
}
render(){
return (
<div>
<div>
<RegisterForm
submitRegister={this.onSubmitRegister}
/>
<h3>{this.state.registerResponse}</h3>
</div>
</div>
)
}
}
/*
Register.propTypes = {
store: PropTypes.object.isRequired
}
*/
const mapStateToProps = (state) => {
return {
form: state.form,
submit_register: state.submit_register,
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({actionSubmitRegister}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);
In mapStateToProps you map store: state.store but in general you use this method to map single props from your state to props in your component, not map the entire store (if this is even possible).
Eg:
form: state.form
The reason you are not able to access the store object in props is because you are not passing it down via props.
Provider from the react-redux library, makes it available to all children down the element tree. Store is made available via React's context API, NOT via props.
"Context is designed to share data that can be considered “global” for a tree of React components."
So in a child component of Provider, we can now do something like
render() {
const { store } = this.context;
console.log(store)
return(
...
)
}
This is the same way that react-redux's connect HOC is able to access the store and subsequently mapStateToProps or utilise the store's dispatch method to mapDispatchToProps.
Also I think Provider requires that it’s child element is a React component.
Check out this tutorial for a more in-depth explanation.
After the input I received above, I reviewed my code and got it to work.
Actually the main issue was on the /client/pages/common/register/index.js file, but I am posting the whole chain for reference:
/client/index.js
nothing to change
/client/App.js
The references to propTypes do not seem to be necessary, so I took them out.
import React from 'react';
import { Provider } from 'react-redux';
import {
BrowserRouter as Router,
Route,
Link,
Switch
} from 'react-router-dom';
import HomePage from './pages/home/';
import AccountPage from './pages/account/';
const Root = ({ store }) => {
return(
<Provider store={store}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<Router>
<div>
<Link to="/account">Account</Link>
<Link to="/">Home</Link>
<div>
<Switch>
<Route path="/account" component={AccountPage} />
<Route path="/" component={HomePage} />
</Switch>
</div>
</div>
</Router>
</div>
</Provider>
)
}
export default Root;
/client/pages/home/index.js
Here both propTypes and connect() do not seem to be required.
import React from 'react';
import Register from '../common/register/';
class Home extends React.Component {
constructor(props){
super(props)
}
render() {
return (
<div>
<h1> Hello World from home! </h1>
<Register />
</div>
);
}
}
export default Home;
/client/pages/common/register/index.js
The main issue here was the onSubmitRegister() method. The promise was not properly setup and I was referencing the action directly instead of using this.props. React do not seem to like that.
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import RegisterForm from './containers/register';
import { actionSubmitRegister } from './actions/';
class Register extends React.Component{
constructor (props) {
super(props);
this.state = {
registerResponse: '',
}
this.onSubmitRegister = this.onSubmitRegister.bind(this);
}
onSubmitRegister (event) {
event.preventDefault();
let submitForm = new Promise((resolve) => {
resolve(this.props.actionSubmitRegister(this.props.form.RegisterForm.values));
});
submitForm.then((result) => {
let data = result.payload.data;
this.setState({registerResponse: data.message});
}).catch((error) => {
console.log(error);
});
}
render(){
return (
<div>
<div>
<RegisterForm
submitRegister={this.onSubmitRegister}
/>
<h3>{this.state.registerResponse}</h3>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
form: state.form,
submit_register: state.submit_register,
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({actionSubmitRegister}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);

React setting up a one-off route

I have an application that uses the same layout for all routes... except one.
One route will be completely different than all others.
So the entire application will have a menu, body, footer, etc.
The one-off route will not have any of that and be a completely separate thing.
How should I set this kinda thing up in a react app? Everything I've ever seen/done always has one main wrapping element that has the routes rendered as children.
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import configureStore from './store'
import App from './components/App'
// import registerServiceWorker from './registerServiceWorker'
import { unregister } from './registerServiceWorker'
const preloadedState = window.__PRELOADED_STATE__ ? window.__PRELOADED_STATE__ : {}
// console.log('window.__PRELOADED_STATE__', window.__PRELOADED_STATE__)
delete window.__PRELOADED_STATE__
const Store = configureStore(preloadedState)
const rootEl = document.getElementById('root')
ReactDOM.hydrate(
<Provider store={Store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
rootEl
)
if(module.hot){
module.hot.accept('./components/App', () => {
ReactDOM.hydrate(
<Provider store={Store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
rootEl
)
})
}
// registerServiceWorker()
unregister()
App.js
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
import { connect } from 'react-redux'
// Components
import AppHelmet from './AppHelmet'
import Notices from './Notices'
import Header from './Header'
import Body from './Body'
import Footer from './Footer'
// Site state
import { getSiteInfo } from '../store/actions/siteInfo'
import { REACT_APP_SITE_KEY } from '../shared/vars'
// CSS
import '../css/general.css'
class App extends Component {
initialAction() {
this.props.getSiteInfo(REACT_APP_SITE_KEY)
}
componentWillMount() {
// On client and site info has not been fetched yet
if(this.props.siteInfo.site === undefined){
this.initialAction()
}
}
render() {
return (
<div>
<AppHelmet {...this.props} />
<Notices />
<div className="body">
<Header />
<Body />
</div>
<Footer />
</div>
)
}
}
const mapStateToProps = (state) => {
return {
siteInfo: state.siteInfo,
user: state.user
}
}
const mapDispatchToProps = (dispatch) => {
return {
getSiteInfo: (siteKey) => dispatch(getSiteInfo(siteKey))
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App))
Body.js
import React, { Component } from 'react'
import { Switch, Route } from 'react-router-dom'
import routes from '../shared/routes'
class Body extends Component {
render() {
return (
<Switch>
{routes.map((route, i) => <Route key={i} {...route} />)}
</Switch>
)
}
}
export default Body
So, as you can see the index.js entry point will render <App />. <App /> will render the main layout, including <Body />, which renders all routes and content.
Cool.
But seeing as I don't want this one-off to render the <App /> layout, I'm not sure how to set this up from index.js. I'm sure it's simple and I'm just not seeing the answer.
One way to achieve what you want is to listen to the router.
You can add the listener into the components you want to hide.
When the listener detects you're on a view where you do not want the components to show, simply don't render them for that view.

Categories