How to load data in Meteor App using React? - javascript

Intro:
I'm newbie in meteor, I have read the documentation and questions here about this issue but the doubt still persist. My main problem is that I cant load data of my MongoDb in client side (what methods to use to load data).
Example:
I have a project that has the following folder structure:
image
In my collections (People.js) I have this:
import { Mongo } from 'meteor/mongo';
export const People = new Mongo.Collection('people');
In the server folder ( main.js ). PS: I can't change this file.
import { People } from '../collections/people';
Meteor.startup(() => {
const TEST_DATA = [
{
something: 'This is a simple example',
}, ... ]
TEST_DATA.forEach(test => People.insert(test));
}
In UI folder (app.jsx)
import React, { Component } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import { Meteor } from 'meteor/meteor';
import {People} from '../collections/people';
export class App extends Component {
render() {
return (
<div>
<h3>Teste </h3>
{ console.log(this.users) }
{ console.log(People.find({}).fetch()) }
{ console.log(Meteor.subscribe('people')) }
</div>
);
}
}
export default withTracker(() => {
return {
users: People.find({}).fetch(),
};
})(App);
cliente folder (main.jsx):
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import { App } from '../ui/App';
Meteor.startup(() => {
render(<App />, document.getElementById('app'));
});
Debug:
I inspected the database and it's populated. The first console.log show undefined , the second an array of length:0, the third an object {stop: ƒ, ready: ƒ, subscriptionId: "mJQHdGxka4xTCX7FZ"} (I think it's returning this because I'm not using publish () on the server to populate the database)
What method should I use to obtain this data?

Change app.jsx to
import React, { Component } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import { Meteor } from 'meteor/meteor';
import {People} from '../collections/people';
export class App extends Component {
render() {
console.log(this.props.users)
return (
<div>
<h3>Teste </h3>
{ this.props.users.map((user) => { return (<div>user.something</div>) }) }
</div>
);
}
}
export default withTracker(() => {
Meteor.subscribe('people');
return {
users: People.find({}).fetch(),
};
})(App);
I've modified the code a little further in the direction that I would assume you'd want it to go. The core change is it use this.props.users. That's where the properties in React components can be found, and where they are places by the function returned by withTracker.
This all assumes that you are still using autopublish (as meteor does in new projects until you remove it). If you have already removed that package, then you need to add
Meteor.publish('people', () => {
return People.find();
});
in your server code.

Related

Using ReactDOM.render in class

I have a very unique issue where I need to render components with different ids on a site, and cannot render all the content under one ID.
I have been able to collect a JSON array through a GET request and have been able to get a for each loop for each array, however I just need to render that particular data with an ID passed from the array.
I have tried to use ReactDom.render in a class but I cannot find how this can be done & and I have current set document.getElementById('modules') to one particular div to begin with to see if that would render, but having no luck.
Any help would be appreciated.
Index.js
`
import { ColorModeScript } from '#chakra-ui/react';
import React, { StrictMode } from 'react';
import { Component } from 'react';
import { ChakraProvider } from "#chakra-ui/react";
import ReactDOM from 'react-dom';
import reportWebVitals from './reportWebVitals';
import * as serviceWorker from './serviceWorker';
import Theme from "./theme";
import Page from "./structure/Page";
import axios from "axios";
class Index extends Component {
constructor(props) {
super(props);
this.state = {
modules: [],
};
}
componentDidMount() {
var currentPath = window.location.pathname;
if (currentPath === '/') {
currentPath = '/home';
}
axios
.get("/lite/modules" + currentPath)
.then((response) => {
const data = response.data;
this.setState({ modules: data });
Object.entries(data).forEach(data1 => {
var ModuleData = data1[1];
this.renderModule(ModuleData);
});
})
.catch((error) => {
console.log(error);
});
}
renderModule(ModuleData){
console.log(ModuleData);
var divID = "module" + ModuleData.type;
ReactDOM.render(
<h1>demo</h1>,
document.getElementById('modules')
);
}
render() {
return (
<ChakraProvider theme={Theme}>
<Page modules={this.state.modules} />
</ChakraProvider>
);
}
}
export default Index;
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorker.unregister();
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more:
reportWebVitals();
`
Render multiple react components with different IDs.
I think you need to add this to your constructor:
this.renderModule = this.renderModule.bind(this)
if you have another server for the DB:
you need to do that when using axios:
axios.get(`${process.env.REACT_APP_SERVER_URL || '**LOCAL_SERVER_URL**'}/...`)

New Components in Application cannot connect to redux

I have created a small application and connected it to Redux. Unfortunately when creating new components and using the same exact code those new components cannot seem to connect to redux and get undefined when accessing it (using mapStateToProps).
I have tried to create new Components and connect them again to no avail. I'm kind of at loss as to why it isn't working especially since the rest of the application can connect and get the state properly
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store} >
<App />
</Provider>
, document.getElementById('root'));
store.js:
const initialState = {
guessedTimezone: '',
timezone: '',
pseudo: '',
};
function rootReducer(state = initialState, action) {
console.log(action);
if (action.type === 'CHANGE_TIMEZONE') {
return Object.assign({}, state, {
timezone: action.timezone,
guessedTimezone: action.guessedTimezone
})
}
if (action.type === 'CHANGE_PSEUDO') {
return Object.assign({}, state, {
pseudo: action.pseudo,
token: action.token
})
}
return state;
}
export default rootReducer;
new Component not connecting:
import React, { Component } from 'react'
import { connect } from 'react-redux'
export class TestPseudo extends Component {
render() {
console.log(this.props.pseudo);
return (
<div>
{this.props.pseudo}
</div>
)
}
}
const mapStateToProps = state => {
return {
pseudo: state.pseudo
}
}
export default connect(mapStateToProps)(TestPseudo)
Here for example this.props.pseudo returns undefined when, if the connection happens, it should return the value if i understand it correctly and yet it shows undefined
EDIT:
App.js as per requested :
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Homepage from './Components/Homepage';
import moment from 'moment';
import moment_timezone from 'moment-timezone';
import HeaderApp from './Components/HeaderApp';
import { TestPseudo } from './Components/TestPseudo';
export class App extends Component {
async componentDidMount() {
let tz = moment.tz.guess(true);
let date = moment(new Date()).local();
let timezone = date['_i'].toString().split('(')[1].split(')')[0];
this.props.dispatch({
type: 'CHANGE_TIMEZONE',
guessedTimezone: tz,
timezone: timezone
})
console.log(`Guessed timezone: ${tz} (${timezone})`);
}
_showHomepage() {
if (this.props.showHomepage && this.props.loaded) {
return (
<div style={styles.mainWindow}>
{/*<Homepage click={this._handleClick} />*/}
<TestPseudo />
</div>
)
}
}
_showHeader() {
return (
<div>
<HeaderApp />
</div>
)
}
render() {
return (
<div>
{this._showHeader()}
{this._showHomepage()}
</div>
)
}
}
const styles = {
mainWindow: {
height: '100vh',
width: '100vw'
}
}
const mapStateToProps = state => {
return {
guessedTimezone: state.guessedTimezone,
timezone: state.timezone,
};
};
export default connect(mapStateToProps)(App);
I call that new Component instead of my old Component. The homepage can connect but not the new one so i think it's not a problem of emplacement
I think its here
import { TestPseudo } from './Components/TestPseudo';
You are importing the non-connected component. Try this
import TestPseudo from './Components/TestPseudo';
For your understanding, exporting as default can be imported like so;
export default Component
import WhateverName from ....
Named export like const or in your case class;
export class Component
import { Component } from ...
So use brackets when Named, and skip brackets when default.

Filepond extracting base64 react js

Trying to get a base64 from image upload and send it to the server. with other values, i can get the value of the hidden inputs. Api is configured to take 2 files at a time, so i cant process the uploads separately at this point
Having trouble mapping the 'data' from the upload image's value i have tried simple mapping methods but still failing.
My code below
import React, { Component } from 'react';
import {connect} from 'react-redux'
import {Button} from 'antd'
import IntlMessages from '../utility/intlMessages';
import authActions from '../../redux/auth/actions'
import { apiUrl } from '../../config';
import { FilePond, registerPlugin } from 'react-filepond';
import 'filepond/dist/filepond.min.css';
import FilePondPluginImagePreview from 'filepond-plugin-image-preview';
import FilePondPluginImageExifOrientation from 'filepond-plugin-image-exif-orientation';
import FilePondPluginImageTransform from 'filepond-plugin-image-transform';
import FilePondPluginFileEncode from 'filepond-plugin-file-encode';
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css';
registerPlugin(FilePondPluginImagePreview,
FilePondPluginImageExifOrientation, FilePondPluginImageTransform, FilePondPluginFileEncode);
const { kycUploadDocs } = authActions;
class FilePondCompnent extends Component {
constructor(props) {
super(props);
this.state = {
files: [],
base64files: []
};
}
handleFilesUpdate() {
let munei = document.querySelector('.filepond--file-wrapper');
if(this.pond.getFiles().length === 2) {
const input = munei.querySelectorAll('input[type="hidden"]')[0]
console.log(input.value)
base64map => {
this.setState({
base64files: input.value.map(req => req.data)
});
}
}
}
handleInit() {
console.log("FilePond instance has initialised", this.pond);
}
render() {
return (
<div className="munei">
{/* Pass FilePond properties as attributes */}
<FilePond
ref={ref => (this.pond = ref)}
files={this.state.files}
allowMultiple={true}
maxFiles={2}
instantUpload={false}
allowRevert={false}
allowFileEncode={true}
// oninit={() => this.handleInit()}
onupdatefiles={fileItems => {
// Set currently active file objects to this.state
this.setState({
files: fileItems.map(fileItem => fileItem.file)
});
this.handleFilesUpdate()
}}
/>
</div>
);
}
}
export default connect((state, ownProps) => ({
// isLoading: state.App.get('isLoading'),
// balances: state.Funds.get(ownProps.fund+'_balances'),
}),
{ kycUploadDocs }
)(FilePondCompnent);
The latest version of the file encode plugin adds two methods to the file items. getFileEncodeBase64String and getFileEncodeDataURL. Those should help achieve what you're trying to do without actually having to read the hidden file input elements.
See docs for further info: https://pqina.nl/filepond/docs/patterns/plugins/file-encode/

Get data from Redux Store after data has been added

I am using a boilerplate called trufflebox's react-auth where
getWeb is called on loading the page (Link to code)
which creates the web3 object (Link to Code)
and stores web3 in the Redux store (Link to code)
Problem: When I retrieve the web3 object from the Redux store, it is undefined, most likely because web3 has not been created yet in Step 2 described above.
What should be the correct way to retrieve web3 from the Redux store only after it has been set?
layouts/test/Test.js
import React, { Component } from 'react';
import store from '../../store';
class Test extends Component {
componentWillMount() {
this.setState({
web3: store.getState().results.payload.web3Instance
})
this.instantiateContract()
}
instantiateContract() {
console.log(this.state.web3) // UNDEFINED!!
}
render() {
return (
<h1>Test</h1>
)
}
}
export default Test
Everything works if I retrieve web3 again without going to the Redux store:
import React, { Component } from 'react';
import getWeb3 from '../../util/web3/getWeb3';
class Test extends Component {
componentWillMount() {
getWeb3
.then(results => {
this.setState({
web3: results.payload.web3Instance
})
this.instantiateContract()
})
}
instantiateContract() {
console.log(this.state.web3)
}
render() {
return (
<h1>Test</h1>
)
}
}
export default Test
Resolve the promise just after creating the store
src/store.js
import getWeb3 from './util/web3/getWeb3';
import { createStore } from 'redux';
//... prepare middlewares and other stuffs , then : create store
const store = createStore(/*....configure it as you want..*/);
// Just after creating store, here the engineering:
getWeb3.then(results => {
// Assuming you have suitable reducer
// Assuming the reducer put the payload in state and accessible via "getState().results.payload.web3Instance"
store.dispatch({ type: 'SAVE_WEB3', payload: results.payload.web3Instance });
});
export default store;
In you ./index.js (where you are rendering the whole app) consider to use Provider component as wrapper to pass store behind the seen and have a singleton store.
src/index.js
import { Provider } from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<Test />
</Provider>
)
Now, in your component, connect HOC will do everything , see comments below :
src/.../Test.js
import { connect } from 'react-redux';
class Test extends Component {
// No need any lifecyle method , "connect" will do everything :)
render() {
console.log(this.props.web3)
return (
<h1>Test</h1>
)
}
}
// Retrieve from Redux STATE and refresh PROPS of component
function mapStateToProps(state) {
return {
web3: state.results.payload.web3Instance // since you are using "getState().results.payload.web3Instance"
}
}
export default connect(mapStateToProps)(Test); // the awesome "connect" will refresh props
Maybe try calling instantiateContract during the componentWillReceiveProps phase. Check out the following...
componentWillMount() {
this.setState({
web3: store.getState().results.payload.web3Instance
});
}
instantiateContract() {
console.log(this.state.web3); // hopefully not undefined
}
componentWillReceiveProps(nextProps) {
if(nextProps.whatever) {
this.instantiateContract();
}
}
render() {
return (
<h1>Test</h1>
)
}
where nextProps.whatever is what you are mapping from redux (not totally sure what this is given your details). Ideally this is getting fed back into your component and when the value either populates or changes, you then call your function
Also, I see a lot of state management here opposed to what I would expect to see done via props. if componentWillReceiveProps(nextProps) is not a good hook given your application architecture, componentDidUpdate(prevProps, prevState) could be a viable alternative.

How to make an AJAX call to get some data and render the result server side? GET data on server -> render result -> send HTML to client

I am trying to figure out the best way to fetch data on the server side and render the resulting template there.
I've looked at some other articles on this -
One thing I saw was to use statics. What does this mean?
Essentially, here's my very basic component code. I know I have componentDidMount there, but I need to replace that somehow so that the request and result set is done server side but don't know how.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actionCreators from '../../actions/fake-data';
class LandingPage extends Component {
componentDidMount() {
this.getFakeData();
}
getFakeData() {
this.props.actions.getFakeData();
}
render() {
console.log(this.props);
return (
<div>
{
this.props.fakeData.fetching ?
<h2>Loading...</h2> :
this.props.fakeData.response.map(item => <p>{item.title}</p>)
}
</div>
);
}
}
function mapStateToProps(state) {
console.log(state);
return {
fakeData: state.fakeData
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actionCreators, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(LandingPage);
Thanks.

Categories

Resources