I'm trying to get a very basic app going using Meteor 1.3, React and React-Router. The pages are rendering but am having issues with getting data passed through. I've done some research but unable to find much with this particular mix including use of the container pattern.
All that the app needs to do is surface all items in the 'Thingies' collection on the Test page. Unsure if it's the data publication, routing, container or something else that's incorrect?
The debugging console lines all show 0 in the collection even though the mongo shell definitely shows items in there.
Structure of my project: http://i.stack.imgur.com/WZXFa.png
things.js
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
export const Thingies = new Mongo.Collection('thingies');
if (Meteor.isServer) {
// This code only runs on the server
Meteor.publish('thingies', function thingiesPublication() {
return Thingies.find();
});
}
Test.jsx
import { Meteor } from 'meteor/meteor';
import { Thingies } from '../../api/things.js';
import { createContainer } from 'meteor/react-meteor-data';
import React, { Component } from 'react';
class TestContainer extends Component {
render(){
console.log('Props: ' + this.props.thingies.length);
let RenderThings = this.props.thingies.map(thing => {
return <li key={thing._id}>{thing.text}</li>
});
return (
<div>
<h1>Test this</h1>
<ul>
{ RenderThings }
</ul>
</div>
);
}
}
TestContainer.propType = {
thingies: React.PropTypes.array
};
export default createContainer(() => {
Meteor.subscribe('thingies');
console.log('Container: ' + Thingies.find({}).count());
return {
thingies: Thingies.find({}).fetch(),
};
}, TestContainer);
routes.jsx
import React from 'react';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
// route components
import App from '../../ui/layouts/App.jsx';
import TestContainer from '../../ui/pages/Test.jsx';
import Index from '../../ui/pages/Index.jsx';
export const renderRoutes = () => (
<Router history={browserHistory}>
<Route path="/" component={ App }>
<IndexRoute component={ Index } />
<Route path="index" component={Index}/>
<Route path="test" component={TestContainer}/>
</Route>
</Router>
);
Navigation.jsx
import React from 'react';
import { IndexLink, Link } from 'react-router';
export const Navigation = () => (
<div>
<h4>Navigation</h4>
<ul>
<li><IndexLink to="/" activeClassName="active">Home</IndexLink></li>
<li><Link to="index" activeClassName="active">Index</Link></li>
<li><Link to="test" activeClassName="active">Test</Link></li>
</ul>
</div>
)
App.jsx
import React, { Component } from 'react';
import { Navigation } from '../components/Navigation.jsx';
const App = ( { children } ) => (
<div>
<Navigation />
{ children }
</div>
)
export default App;
Many thanks in advance. I'm sure I'm missing something obvious!
I would try to change the Test.jsx Container code a little bit:
export default createContainer(() => {
if (Meteor.subscribe('thingies').ready()) {
console.log('Container: ' + Thingies.find({}).count());
return {
thingies: Thingies.find({}).fetch()
};
} else {
return {
thingies: null
};
}
}, TestContainer);
EDIT: Try replace your publish method with:
if (Meteor.isServer) {
// This code only runs on the server
Meteor.publish('thingies', function() {
return Thingies.find({});
});
}
Related
I have a create profile page and an auth page (where one enters a code sent by text). I'd like to navigate to the auth page, when a profile is created.
I have a component for the create profile page and one for the auth page.
Based on the example here.
Relevant code:
// CreateProfileComponent.js
import React from 'react';
..
import { withRouter } from "react-router";
class CreateProfile extends React.Component {
constructor(props) {
super(props);
}
handleCreateProfile(e) {
e.preventDefault();
if (condition) {
createProfile(...);
this.props.history.push('/auth');
} else {
// re-render
}
}
render() {
return (
// data-testid="create-profile" - workaround (https://kula.blog/posts/test_on_submit_in_react_testing_library/) for
// https://github.com/jsdom/jsdom/issues/1937
<form onSubmit={this.handleCreateProfile.bind(this)} data-testid="create-profile">
...
</form>
);
}
}
export default withRouter(CreateProfile);
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import CreateProfile from './CreateProfileComponent';
import { TokenEntry } from './TokenEntryComponent';
ReactDOM.render(
<Router>
<ProtectedRoute exact path="/" component={ActivityList} />
<Route path="/login" component={CreateProfile.WrappedComponent} />
<Route path="/auth" component={TokenEntry} />
</Router>,
document.getElementById('root')
);
//createprofilecomponent.test.js
import React from 'react';
import {
LocationProvider,
createMemorySource,
createHistory
} from '#reach/router';
import { render, screen, fireEvent } from '#testing-library/react';
import CreateProfile from '../CreateProfileComponent';
const source = createMemorySource('/login');
const history = createHistory(source);
let displayName = null;
let phoneNumber = null;
let legalAgreement = null;
global.window = { location: { pathname: null } };
function Wrapper({children}) {
return <LocationProvider history={history}>{children}</LocationProvider>;
}
beforeEach(() => {
render(
<CreateProfile.WrappedComponent location={'/'} />,
{ wrapper: Wrapper }
);
});
it("navigates to /auth when good data entered", () => {
// setup
fireEvent.submit(screen.getByTestId('create-profile'));
expect(global.window.location.pathname).toEqual('/auth');
});
I'm getting
TypeError: Cannot read property 'push' of undefined
during tests and in Chrome.
What am I missing?
Use the react-router-dom
import { withRouter } from "react-router-dom";
Changed
export default withRouter(CreateProfile);
To
export const CreateProfileWithRouter = withRouter(CreateProfile);
Changed
<Route path="/login" component={CreateProfileWithRouter.WrappedComponent} />
To
<Route path="/login" component={CreateProfileWithRouter} />
Pull request created to include an example in withRouter's documentation.
Example gist
I am trying to make a single web application. Basically, I am trying to use the ReactRouter to display what is passed as a Route Parameter. However, I am unable to do that. To check if somethings wrong, I decided to console.log out this.props.match, still nothing shows up. Could someone explain what the problem is? And a possible get around?
My code is-
import React from 'react';
export default class Post extends React.Component {
state = {
id: null
}
componentDidMount(props) {
console.log(this.props.match);
}
render = () => {
return (<div>Hello WOrld</div>)
}
}
The App.js file:
import React, { Fragment, Component } from 'react';
import Navbar from './components/Navbar';
import Home from './components/Home';
import Contact from './components/Contact';
import About from './components/About'
import Post from './components/Post';
import { BrowserRouter, Route } from 'react-router-dom';
class App extends Component {
render = () => {
return (
<BrowserRouter>
<div className="App">
<Navbar />
<Route exact path="/" component={Home} />
<Route path="/contact" component={Contact} />
<Route path="/about" component={About} />
<Route path="/:post-id" component = {Post} />
</div>
</BrowserRouter>
);
}
}
export default App;
I just ran your code on my end, it looks like the problem is using /:post-id. I changed that to /:pid and it worked. I got the below object when I console log this.props.match
{
"path":"/:pid",
"url":"/1",
"isExact":true,
"params":
{
"pid":"1"
}
}
I hope this helps.
You have to load the component with router
try this
import { withRouter } from 'react-router-dom';
class Post extends React.Component {
state = {
id: null
}
componentDidMount(props) {
console.log(this.props.match);
}
render = () => {
return (<div>Hello WOrld</div>)
}
}
export default withRouter(Post);
I'm trying to get a component that will appear after a second.
The component itself simply has only H1.
I use a router so the most correct solution would seem to me to use the history.push but it does not work for me, what am I doing wrong?
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import {withRouter} from 'react-router-dom'
import './App.css';
import Hello from './comp/Hello';
class App extends Component {
render() {
return (
<Router>
<div className="App">
</div>
<Route path="/hello" component={Hello} />
</Router>
);
}
componentWillMount() {
this.hello()
}
hello(){
setTimeout(() => {
this.props.history.push('/hello')
}, 1000);
}
}
export default App;
===========================the Component========================
import React, { Component } from 'react';
class App extends Component {
render() {
return (
<div className="App">
<h1>hi</h1>
</div>
);
}
}
export default App;
i think you need to wrap you component with withRouter hook .
try like below,
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import {withRouter} from 'react-router-dom'
import './App.css';
import Hello from './comp/Hello';
class App extends Component {
render() {
return (
<Router>
<div className="App">
</div>
<Route path="/hello" component={Hello} />
</Router>
);
}
componentWillMount() {
this.hello()
}
hello(){
setTimeout(() => {
this.props.history.push('/hello')
}, 1000);
}
}
export default withRouter(App);
It is hard to see exactly what is going on, usually only child components suffer from these kinds of errors in my experience, because the props.history is not being passed on. The Apps component should in most cases be the main component though.
Regardless, in the meantime I suggest you try this approach:
import { BrowserRouter as Router, Route, Link, Redirect } from "react-router-dom";
...
hello(){
setTimeout(() => {
return <Redirect to={{
pathname: "/hello",
state: {
optionalStateToPass,
},
}} />;
}, 1000);
}
...
For a component to receive the history prop, you need 2 things:
Router must be at the top level, above all calls to Route, Link, and withRouter
The component that needs the prop history must be rendered by a Route, e.g. <Route component={ComponentThatNeedsHistoryProp} ...> OR wrap it with withRouter.
In your case, it seems you cannot rely on the route props to be passed, so we need to use withRouter.
It should look like this (imports not shown):
// App.js
class App = () => (
<div className="App">
<Router>
<Hello />
</Router>
</div>
)
// Hello.js
class Hello extends Component {
render() {
return <h1>hi</h1>
}
componentWillMount() {
this.hello()
}
hello(){
setTimeout(() => {
this.props.history.push('/hello')
}, 1000);
}
}
export default withRouter(Hello);
Seeing issues while developing a real time wallboard using react . I am displaying 2 components one after another Dashboard1( a table with data), Dashboard2(another table with data) with 3 secs time interval. My parent component is Dashboard it connects to my firestore DB to receive real-time updates and sends this data to Dashboard1 component and then Dashboard 1 renders its data and after 3 seconds calls Dashboard2 with the same data passed to it by Dashboard using props.history.push().I am seeing 2 issues here. The component Dashboard 2 is always rendered above Dashboard1.Like when i scroll down the page ,i can still see Dashboard1 at the bottom. How to clear off the page before rendering Dashboard1 and 2 .So that i just see a single component at a time on the screen.Below is my code for App, Dashboard ,Dashboard1 and Dashboard2.I am also seeing Dashboard 2 is being rendered multiple times in the console logs.
Kindly help me to fix these 2 issues:
App.js:
import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom'
import Dashboard from './components/Dashboard'
import Dashboard1 from './components/Dashboard1'
import Dashboard2 from './components/Dashboard2'
class App extends Component {
render() {
console.log('App')
return (
<BrowserRouter>
<div className="App">
<Route exact path='/' component={Dashboard} />
<Route exact path='/Dashboard1' component={Dashboard1} />
<Route exact path='/Dashboard2' component={Dashboard2} />
<Dashboard />
</div>
</BrowserRouter>
)
}
}
export default(App)
Dashboard.js:
import React, { Component } from 'react';
import { BrowserRouter, Route, Redirect } from 'react-router-dom'
import Dashboard1 from './Dashboard1'
import Dashboard2 from './Dashboard2'
import { connect } from 'react-redux'
import { firestoreConnect } from 'react-redux-firebase'
import { compose } from 'redux'
class Dasboard extends Component {
render() {
console.log('Dashboard')
const { agents } = this.props
if (!agents) {
return null
}
return (
<Dashboard1 data={agents}/>
)
}
}
const mapStateToProps = (state) => {
return {
agents: state.firestore.data.agent_groups
}
}
export default compose(
connect(mapStateToProps),
firestoreConnect([
{ collection: 'agent_groups' }
])
)(Dasboard)
Dashboard1.js:
import React from 'react'
import Table from '../layouts/Table'
import { withRouter } from 'react-router-dom'
const Dashboard1 = (props) => {
console.log('Dashboard1')
setTimeout(() => {
props.history.push({
pathname: '/Dashboard2',
state: { data: props.data.WFM_Inbound_Sales.agents }})
}, 3000);
return (
<div className="dashboard1">
<Table
data={props.data.WFM_Inbound_Sales.agents}
headers={[
{
name: 'Agent',
prop: 'name'
},
{
name: 'Total calls',
prop: 'InboundCalls'
}
]}
/>
</div>
)
}
export default withRouter(Dashboard1)
Dashboard2.js:
import React from 'react'
import Table from '../layouts/Table'
import { withRouter } from 'react-router-dom'
const Dashboard2 = (props) => {
console.log('Dashboard2')
setTimeout(() => {
props.history.push('/')
}, 3000);
return (
<div className="dashboard2">
<Table
data={props.location.state.data}
headers={[
{
name: 'Agent',
prop: 'name'
},
{
name: 'Status',
prop: 'state'
},
{
name: 'Time in status',
prop: 'TimeInCurrentState'
}
]}
/>
</div>
)
}
export default withRouter(Dashboard2)
You need to use switch which will render only one route of the given set like this
class App extends Component {
render() {
console.log('App')
return (
<BrowserRouter>
<div className="App">
<Switch>
<Route exact path='/' component={Dashboard} />
<Route exact path='/Dashboard1' component={Dashboard1} />
<Route exact path='/Dashboard2' component={Dashboard2} />
</Switch>
<Dashboard />
</div>
</BrowserRouter>
)
}
}
Check the doc here
I've been developing an idea but am getting stuck on something unusual (my brain hurts on react-router).
I am trying to dynamically render a list of items using .map from a returned object (of multiple similar objects) and appending them to the render(){return(<div />)}.
I just dont know another way than call a function then .map the result for this callback.
I think that the way I'm doing this means the rendered items lose context. The react-router <Link /> will function as expected in the normal flow (placed inside the render(){return(<div />)} ) but not when the item is created from outside of the render. I have posted the error below the code.
I have read Many different ways of getting around this using context and location/history and withRouter. Frankly I'm lost.
I would appreciate if someone could look at my example below and guide me in the right direction.
A few notes:
- main focus appears to be in mystuff
- i have many unnecessary imports i know
- stripped down for clarity, i would get lost otherwise
index
import _ from 'lodash';
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Provider } from 'react-redux';
import { store, history } from './store';
import Main from './Main';
import { routyr } from './Menu';
// remaining paths in Menu.js (routyr) for menu visibility
const router = (
<Provider store={store}>
<Router history={history}>
<Route path="/" component={Main}>
{routyr}
</Route>
</Router>
</Provider>
)
render (router, document.getElementById('app'));
Main
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from './actionCreators';
import App from './app';
function mapStateToProps(state){
return{
info: state.info,
myProfile: state.myProfile
}
}
function mapDispatchToProps(dispatch){
return { actions: bindActionCreators(actionCreators, dispatch) }
}
const Main = connect(mapStateToProps, mapDispatchToProps)(App);
export default Main;
routyr
import React from 'react';
import { Link } from 'react-router';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Provider } from 'react-redux';
import { store, history } from './store';
//pages
import App from './app';
import Landing from './Landing';
import Me from './mystuff';
import ViewStuff from './viewStuff';
//Routes for index.js
export const routyr = (
<span>
<IndexRoute component={Landing} />
<Route path="/myStuff" component={Me} />
<Route path="/viewStuff" component={ViewStuff} />
</span>
)
//Menu types
//loaded by app.js
export const menuLoggedIn = (
<div className="MainMenu">
<Link to='/' className="buttonA green">Home</Link>
<Link to='myStuff' className="buttonA green">My Stuff</Link>
</div>
);
export const menuLoggedOut = (
<div className="MainMenu">
<Link to='/login' className="buttonA green">Login</Link>
</div>
);
app
import React from 'react';
import _ from 'lodash';
import { Link } from 'react-router';
import auth from './auth';
import Landing from './Landing';
import Header from './Header';
import { menuLoggedIn, menuLoggedOut } from './Menu';
export default class App extends React.Component {
constructor(){
super();
this.state={
auth: auth.loggedIn(),
menu: null
};
}
componentWillMount(){
if (this.state.auth==true) {
this.setState({
menu: menuLoggedIn
})
}else{
this.setState({
menu: menuLoggedOut
});
}
}
render(){
return (
<div>
<Header />
{this.state.menu}<br />
<div id="view">
{React.cloneElement(this.props.children, this.props)}
</div>
</div>
);
}
};
mystuff
import React, { PropTypes } from 'react';
import { render } from 'react-dom';
import { Link } from 'react-router';
import { withRouter } from 'react-router';
import { Provider } from 'react-redux';
import * from './whacks';
export default class Me extends React.Component{
constructor(){
super();
}
componentDidMount() {
function listThem(oio){
oio.map(function(ducks){
render(
<div className="ListItem">
<Link to="/viewStuff"> _BROKEN_ View Stuff</Link>
<div className="listLabel">{ducks.type}</div>
<h3>{ducks.description.title}</h3>
{ducks.description.long}
</div>, document.getElementById('fishes').appendChild(document.createElement('div'))
);
});
}
var some = new Whacks();
some.thing(more, (close, open) => {
if(close){
console.log(close));
} else {
doIt(open);
}
});
}
render(){
return(
<div>
<Link to="viewStuff"> _WORKING_ View Stuff</Link>
<div id="fishes">
</div>
</div>
)
}
}
store
import { createStore, compose } from 'redux';
import { syncHistoryWithStore } from 'react-router-redux';
import { browserHistory } from 'react-router';
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
/*-------ROOT REDUCER---------*/
/*-------DEFAULT STATES---------*/
/*-------CREATE STORE---------*/
/*-------INTEGRATE HISTORY---------*/
import me from './reducers/obj';
import myProfile from './reducers/myProfile';
const rootReducer = combineReducers(
{
routing: routerReducer,
me,
myProfile
}
);
//TEMP remove harcoded var
const uuidSet = "fa78d964";
export const defaultState = {
uuid: uuidSet,
};
export const store = createStore(rootReducer, defaultState, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export const history = syncHistoryWithStore(browserHistory, store);
actionCreators
export function me (obj){
return {
type: "ADD_OBJECTLIST",
obj
}
}
export function myProfile (dump){
return {
type: "MY_DATA",
dump
}
}
from package.json
"react-redux": "^5.0.2",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.7",
"redux": "^3.6.0",
error
Uncaught Error: s rendered outside of a router context cannot navigate.
#UG,
I have tried the following in mystuff:
constructor(){
super();
this.state={
oio: {}
};
}
and
some.thing(more, (close, open) => {
if(close){
console.log(close));
} else {
this.setState({
oio: open
});
}
});
and
render(){
let flat = this.state.oio;
flat.map(function(ducks){
return (
<div className="ListItem">
<Link to="/viewStuff">View Stuff</Link>
<div className="listLabel">{ducks.type}</div>
<h3>{ducks.description.title}</h3>
{ducks.description.long}
</div>
)
})
}
and receive
Uncaught TypeError: flat.map is not a function
at Me.render
I am not sure if I get your issue completely. But I think you want to use Link inside render() method of myStuff
You can change that to following :
render(){
return(
<div>
<Link to="viewStuff"> _WORKING_ View Stuff</Link>
<div id="fishes">
{
oio.map(function(ducks){
return (
<div className="ListItem">
<Link to="/viewStuff"> _BROKEN_ View Stuff</Link>
<div className="listLabel">{ducks.type}</div>
<h3>{ducks.description.title}</h3>
{ducks.description.long}
</div>
);
}
</div>
</div>
)
}
As per the comment from James,
You should use react state to maintain oio object.
constructor() {
super();
//init
this.setState({oio : {}});
}
and update the state in async call, when state updates, component can be rerendered.
Huge thanks to UG_ for smacking me in the ear with state.
I have pulled in a component and created each components props from the callback objects.
My Working solution is as follows in mystuff:
constructor(props){
super(props);
this.state={
oio: []
}
}
componentDidMount() {
let listThem = (stuff) => {
let ioi = [];
stuff.forEach(function(dood, index, array) {
let lame = <MyItem plop={dood} key={index} />;
ioi.push(lame);
});
return (
this.setState({
oio: ioi
})
);
}
var some = new Whacks();
some.thing(more, (close, open) => {
if(close){
console.log(close));
} else {
listThem(open);
}
});
}
render(){
return(
<div>
{this.state.oio}
</div>
)
}
Which renders a new copy of the MyItem component with props from each returned object. So now my returned items contain context!