Tracker.autorun not working inside componentDidMount of react - reactjs

Tracker.autorun not working inside componentDidMount of react when I specify the projection (fields) for output. But the same works when I dont have any projection on mongo query.
This works:
Meteor.subscribe('quotes');
this.quotesTracker = Tracker.autorun(() => {
const quotes = Quotes.find(
{instrument_token: 12374274},
{
sort: {timestamp: 1},
limit: 5000
}
).fetch();
This doesnt work
Meteor.subscribe('quotes');
this.quotesTracker =Tracker.autorun(() => {
const quotes = Quotes.find(
{instrument_token: 12374274},
{
fields: {
last_price: 1,
timestamp: 1,
},
sort: {timestamp: 1},
limit: 5000
}
).fetch();
What am I missing here?

I don't think Meteor's tracker works well with ReactJS, as their mechanism of re-render is different.
You might want to use this package.
https://github.com/meteor/react-packages/tree/devel/packages/react-meteor-data
You can use it like so.
import { Meteor } from 'meteor/meteor';
import { mount } from 'react-mounter';
import { withTracker } from 'meteor/react-meteor-data';
import { IndexPage } from "./index-page";
FlowRouter.route('/', {
action: () => {
mount(IndexPageContainer, {});
}
});
export const IndexPageContainer = withTracker(() => {
Meteor.subscribe('whatever');
return {
Meteor: {
collection: {
whatever: Whatever.find().fetch()
},
user: Meteor.user(),
userId: Meteor.userId(),
status: Meteor.status(),
loggingIn: Meteor.loggingIn()
}
};
})(IndexPage);
Where IndexPage is your actual component.
You can then access the db by this.props.Meteor.collection.whatever.find()

Related

Args argument to storybook template is wrong shape

I'm trying to follow https://storybook.js.org/docs/react/writing-stories/introduction and hitting a snag when trying to do the Template example. My code is:
import { ComponentMeta, ComponentStory } from '#storybook/react';
import React, { FC } from 'react';
interface FooProps {
myArg: string;
}
const Foo: FC<FooProps> = ({ myArg }) => <p>{myArg}</p>;
Foo.displayName = 'Foo';
export default {
component: Foo,
title: 'Foo',
} as ComponentMeta<typeof Foo>;
const Template: ComponentStory<typeof Foo> = (args) => {
console.log(args);
return <Foo {...args} />;
};
export const Default = Template.bind({});
Default.args = { myArg: 'Foo' };
However, the args argument that's passed to Template is a complex object that describes the story and has nested under it args.args which is what I'd want to pass to my component. Trying to use that throws a TS error though, but also from looking at the docs and GH issues, it seems like people are successfully using this paradigm, so I'm not sure why it's failing for me.
I'm using the latest storybook version that's been released (6.5.13), and my configuration is:
module.exports = {
stories: [
'./**/*.stories.#(js|jsx|ts|tsx)',
],
addons: [
{
name: '#storybook/addon-postcss',
options: {
postcssLoaderOptions: {
implementation: require('postcss'),
},
},
},
],
framework: '#storybook/react',
};

Learning React with Typescript

I have used observables in Angular, but I am having an issue in finding good examples on how to uses observables with React using Javascript. My end goal is to use hard coded data in a service.ts example
import { of, Observable } from 'rxjs';
export interface Volunteer {
firstName: string;
lastName: string;
totalHoursLogged: number;
}
const tempVolunteers: Volunteer[] = [
{ firstName: 'John', lastName: 'Smith', totalHoursLogged: 85 },
];
export const getAllVolunteers = (): Observable<Volunteer[]> => of(tempVolunteers);
I want to make a list component to build the mock data as it is push from the observable.
I would export the react component into the App.tsx and display the
mock data
If anyone knows of good resources or has any advice, please let me know.
Thanks
You can try this example (without Typescript for simplicity):
source file:
import { of } from 'rxjs';
const tempVolunteers = [
{ firstName: 'John', lastName: 'Smith', totalHoursLogged: 85 },
];
export const getAllVolunteers = of(tempVolunteers);
React app file:
import React, { Component } from 'react';
import {
getAllVolunteers,
} from '../RxJS';
class App extends Component {
state = {
list: [],
};
componentDidMount() {
getAllVolunteers.subscribe((item) => {
this.setState({
list: [...this.state.list, ...item],
});
});
}
render() {
return (
<div>
<ul>
{ this.state.list
.map(({ firstName, lastName, totalHoursLogged }) =>
(
<div key={lastName}>
{`${firstName} ${lastName} - ${totalHoursLogged}`}
</div>
),
)
}
</ul>
</div>
);
}
}
export default App;

Redux Store and nested JSON from Axios API

I tried every possible variation of this code, but I don't really manage to get whatever the API fetched into my data store. I am absolutely stuck and would appreciate some help.
I think I just don't get the essential part of this construct and I would really like to understand how it works properly.
The data looks like this - it's basically a simple JSON (from a django restframework API) with some nested elements:
EDIT 2 (changed JSON to screenshot of axios API/ Redux action)
My Redux action - works perfectly fine. console.log pulls exactly the data from above (with correct inputs) :
// ./action/plan.js
import axios from 'axios';
export function fetchBudgets(){
return function(dispatch){
axios.get("/api/budgets/")
.then((response) => {
console.log(response)
dispatch({ type: "FETCH_BUDGETS", budgets: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_DATA_REJECTED", budgets: err})
})
}
}
So until now, everything seems fine. The problems starts with the reducer - as I am not sure how to model the reducer to use the nested data.
My reducer:
// ./reducer/plan.js
const initialState = {}
export default function budgets(state=initialState, action) {
switch (action.type) {
case 'FETCH_BUDGETS':
console.log(action)
return {
...state,
id: action.budgets.id,
value_jan: action.budgets.value_jan,
value_feb: action.budgets.value_feb,
value_mar: action.budgets.value_mar,
value_apr: action.budgets.value_apr,
value_may: action.budgets.value_may,
value_jun: action.budgets.value_jun,
value_jul: action.budgets.value_jul,
value_aug: action.budgets.value_aug,
value_sep: action.budgets.value_sep,
value_oct: action.budgets.value_oct,
value_nov: action.budgets.value_nov,
value_dec: action.budgets.value_dec,
p_version: action.budgets.p_version,
entry_time: action.budgets.entry_time,
campaign: {
...state.campaign, ...action.budgets.campaign
},
segment: {
...state.segment, ...action.budgets.segment
},
touch_point: {
...state.touch_point, ...action.budgets.touch_point
},
year: {
...state.year, ...action.budgets.year
},
user: {
...state.user, ...action.budgets.user
}
}
default:
return state
}
}
I already cannot display data in here - so this.props.fetchBudgets() doesn't seem to fetch any data.
My .jsx App
//./container/PlanContainer.jsx
import React, { Component } from 'react';
import {connect} from 'react-redux';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
import 'jquery';
import 'popper.js'
import 'bootstrap';
import 'underscore'
import _ from 'lodash'
import {plan} from "../actions";
const columns = [
{ dataField: 'id', text: 'ID', hidden: true},
{ dataField: 'year', text: 'Year', editable: false},
{ dataField: 'segment', text: 'Segment', editable: false},
{ dataField: 'campaign.name',text: 'Campaign', editable: false},
{ dataField: 'touch_point',text: 'Touchpoint', editable: false},
{ dataField: 'value_jan',text: 'Jan'},
{ dataField: 'value_feb',text: 'Feb'},
{ dataField: 'value_mar',text: 'Mar'},
{ dataField: 'value_apr',text: 'Apr'},
{ dataField: 'value_may',text: 'May'},
{ dataField: 'value_jun',text: 'Jun'},
{ dataField: 'value_jul',text: 'Jul'},
{ dataField: 'value_aug',text: 'Aug'},
{ dataField: 'value_sep',text: 'Sep'},
{ dataField: 'value_oct',text: 'Oct'},
{ dataField: 'value_nov',text: 'Nov'},
{ dataField: 'value_dec',text: 'Dec'},
{ dataField: 'user',text: 'User'},
];
const RemoteCellEdit = (props) => {
const { columns, data, keyField } = props
const cellEdit = {
mode: 'click',
errorMessage: props.errorMessage,
blurToSave: true
};
return (
<div>
<BootstrapTable
remote={ { cellEdit: true } }
keyField = { keyField }
data={ data }
columns={ columns }
/>
</div>
);
};
class PlanContainer extends React.Component {
componentDidMount() {
this.props.fetchBudgets();
console.log(this.props.fetchBudgets())
}
render() {
return (
<div>
<RemoteCellEdit
data={ this.props.budgets }
columns = { columns }
keyField = 'id'
/>
</div>
);
}
}
const mapStateToProps = state => {
return {
budgets: state.budgets,
}
}
const mapDispatchToProps = dispatch => {
return {
fetchBudgets: () => {
dispatch(plan.fetchBudgets());
},
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PlanContainer);
Finally, my store - according to the console.log nothing is beeing passed:
// .Planning.jsx
import React from "react"
import { hot } from 'react-hot-loader'
import { render } from "react-dom"
import {
createStore,
compose,
applyMiddleware,
combineReducers,
} from "redux"
import { Provider } from "react-redux"
import thunk from "redux-thunk"
import PlanContainer from "./containers/PlanContainer"
import reducerApp from "./reducers";
import Sidebar from "./components/Sidebar"
import axios from 'axios';
import axiosMiddleware from 'redux-axios-middleware';
let store = createStore(reducerApp, applyMiddleware(thunk, axiosMiddleware(axios)));
console.log(store)
class Planning extends React.Component {
render() {
return (
<Sidebar>
<Provider store={store}>
<PlanContainer />
</Provider>
</Sidebar>
)
}
}
render(<Planning />, document.getElementById('Planning'))
Again, I would appreciate as I've been stuck on this issue for quite some time and I really want to understand how to do this properly.
Edit:
Here's a screenshot of my browser: 1st element is the store, second in the .jsx app, 3rd of the action (that looks perfectly fine) and 4th of the action in the reducer.
PlanContainer is messed up. Here's how:
componentDidMount() {
this.budgets = this.props.fetchBudgets();
}
this.budgets is pointing to the value returned by this.props.fetchBudgets() which, in this case, is a Promise, and not the actual data.
state = {
data: this.budgets
};
state now holds the promise, not the data.
render() {
return (
<div>
<RemoteCellEdit
data={ this.state.data }
...
}
So data here is not the actual data but the promise.
The confusion is happening because you are mixing redux state with react state. Use one or the other, not both (there are expcetions to this but not in this particular scenario).
There are some more issues with PlanContainer which are not clear as to whether they are real issues, or just a result of code ommission in OP.
See annotations below:
class PlanContainer extends React.Component {
componentDidMount() {
this.props.fetchBudgets();
}
constructor(props) {
... // removed for brevity, use the same code as you have right now
}
render() {
return (
<div>
<RemoteCellEdit
data={ this.props.budgets}
columns = { this.columns }
keyField = 'id'
errorMessage={ /* should come from props.data or similar - it's not in state */ }
/>
<tbody>
{this.props.budgets} /* not sure what this is for - I assumed RemoteCellEdit is the one rendering the data */
</tbody>
</div>
);
}
}
Fixing these should set you on the correct course. Good luck!

Registering React Native Code Push with React Native Navigation by Wix

I use react-native-code-push. which is:
This plugin provides client-side integration for the CodePush service,
allowing you to easily add a dynamic update experience to your React
Native app(s).
but In some of native implementations of navigation like react-native-navigation there isn't any root component.
the app will start calling a function like this:
// index.js
import { Navigation } from 'react-native-navigation';
Navigation.startTabBasedApp({
tabs: [
{
label: 'One',
screen: 'example.FirstTabScreen', // this is a registered name for a screen
icon: require('../img/one.png'),
selectedIcon: require('../img/one_selected.png'), // iOS only
title: 'Screen One'
},
{
label: 'Two',
screen: 'example.SecondTabScreen',
icon: require('../img/two.png'),
selectedIcon: require('../img/two_selected.png'), // iOS only
title: 'Screen Two'
}
]
});
// or a single screen app like:
Navigation.registerComponent('example.MainApplication', () => MainComponent);
Navigation.startSingleScreenApp({
screen: {
screen: 'example.MainApplication',
navigatorButtons: {},
navigatorStyle: {
navBarHidden: true
}
},
})
since there is no root component, It's not clear where should I call CodePush, since normally I should wrap my whole root component with CodePush like a higher order component.
what I used to do was:
// index.js
class MyRootComponent extends Component {
render () {
return <MainNavigator/> // a navigator using react-navigation
}
}
let codePushOptions = {
checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME,
installMode: CodePush.InstallMode.ON_NEXT_RESUME
}
export default CodePush(codePushOptions)(MyRootComponent)
Is there a proper way to solve this problem!?
I know I could do this:
Navigation.registerComponent('example.MainApplication', () => CodePush(codePushOptions)(RootComponent));
Navigation.startSingleScreenApp({
screen: {
screen: 'example.MainApplication',
navigatorButtons: {},
navigatorStyle: {
navBarHidden: true
}
},
})
but then I should use a Navigator only for projecting my root component, and It doesn't look like a good idea. I think this problem probably has a best-practice that I'm looking for.
UPDATE
I think there are some complications registering a tab navigator inside a stacknavigator in react-native-navigation at least I couldn't overcome this problem. example tabBasedApp in react-native-navigation with react-native-code-push, will be all that I need.
Thanks for the previous code snippets. I was able to get code push check on app resume and update immediately with react-native-navigation V2 with the below code without requiring wrapper component for codePush. This is the relevant part of the app startup logic.
Navigation.events().registerAppLaunchedListener(() => {
console.log('Navigation: registerAppLaunchedListener ')
start()
})
function checkCodePushUpdate () {
return codePush.sync({
checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,
installMode: codePush.InstallMode.IMMEDIATE,
deploymentKey: CODEPUSH_KEY,
})
}
function start () {
checkCodePushUpdate ()
.then(syncStatus => {
console.log('Start: codePush.sync completed with status: ', syncStatus)
// wait for the initial code sync to complete else we get flicker
// in the app when it updates after it has started up and is
// on the Home screen
startApp()
return null
})
.catch(() => {
// this could happen if the app doesn't have connectivity
// just go ahead and start up as normal
startApp()
})
}
function startApp() {
AppState.addEventListener('change', onAppStateChange)
startNavigation()
}
function onAppStateChange (currentAppState) {
console.log('Start: onAppStateChange: currentAppState: ' + currentAppState)
if (currentAppState === 'active') {
checkCodePushUpdate()
}
}
function startNavigation (registered) {
console.log('Start: startNavigation')
registerScreens()
Navigation.setRoot({
root: {
stack: {
children: [{
component: {
name: 'FirstScreen,
},
}],
},
},
})
}
I got it working this way, although this is for RNN v2
// index.js
import App from './App';
const app = new App();
// App.js
import CodePush from 'react-native-code-push';
import { Component } from 'react';
import { AppState } from 'react-native';
import { Navigation } from 'react-native-navigation';
import configureStore from './app/store/configureStore';
import { registerScreens } from './app/screens';
const appStore = configureStore();
registerScreens(appStore, Provider);
const codePushOptions = {
checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME,
updateDialog: true,
installMode: CodePush.InstallMode.IMMEDIATE
};
export default class App extends Component {
constructor(props) {
super(props);
// Set app state and listen for state changes
this.appState = AppState.currentState;
AppState.addEventListener('change', this.handleAppStateChange);
this.codePushSync();
Navigation.events().registerAppLaunchedListener(() => {
this.startApp();
});
}
handleAppStateChange = nextAppState => {
if (this.appState.match(/inactive|background/) && nextAppState === 'active') {
this.handleOnResume();
}
this.appState = AppState.currentState;
};
codePushSync() {
CodePush.sync(codePushOptions);
}
handleOnResume() {
this.codePushSync();
...
}
startApp() {
Navigation.setRoot({
root: {
stack: {
children: [
{
component: {
name: 'MyApp.Login'
}
}
]
}
}
});
}
}
// app/screens/index.js
import CodePush from 'react-native-code-push';
import { Navigation } from 'react-native-navigation';
import Login from './Login';
function Wrap(WrappedComponent) {
return CodePush(WrappedComponent);
}
export function registerScreens(store, Provider) {
Navigation.registerComponentWithRedux(
'MyApp.Login',
() => Wrap(Login, store),
Provider,
store.store
);
...
}
I found the answer myself.
Look at this example project structure:
.
├── index.js
├── src
| └── app.js
└── screens
├── tab1.html
└── tab2.html
you can register you code-push in index.js.
//index.js
import { AppRegistry } from 'react-native';
import App from './src/app';
import CodePush from 'react-native-code-push'
let codePushOptions = {
checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME,
installMode: CodePush.InstallMode.ON_NEXT_RESUME
}
AppRegistry.registerComponent('YourAppName', () => CodePush(codePushOptions)(App));
now you can start react-native-navigation in app.js like this:
import {Navigation} from 'react-native-navigation';
import {registerScreens, registerScreenVisibilityListener} from './screens';
registerScreens();
registerScreenVisibilityListener();
const tabs = [{
label: 'Navigation',
screen: 'example.Types',
icon: require('../img/list.png'),
title: 'Navigation Types',
}, {
label: 'Actions',
screen: 'example.Actions',
icon: require('../img/swap.png'),
title: 'Navigation Actions',
}];
Navigation.startTabBasedApp({
tabs,
tabsStyle: {
tabBarBackgroundColor: '#003a66',
tabBarButtonColor: '#ffffff',
tabBarSelectedButtonColor: '#ff505c',
tabFontFamily: 'BioRhyme-Bold',
},
appStyle: {
tabBarBackgroundColor: '#003a66',
navBarButtonColor: '#ffffff',
tabBarButtonColor: '#ffffff',
navBarTextColor: '#ffffff',
tabBarSelectedButtonColor: '#ff505c',
navigationBarColor: '#003a66',
navBarBackgroundColor: '#003a66',
statusBarColor: '#002b4c',
tabFontFamily: 'BioRhyme-Bold',
}
});

How to re-render child component In this situation?

Problem
If I click a number, the number should increase but not increase.
As you can see, child components are not re-rendered. (If I change the key of the 'li' element to Math.random() it works fine.)
How can I solve this situation?
An example was posted on https://codesandbox.io/s/p5q30rxk47
Thanks for reading.
Source code
The source code is roughly as follows.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Parent from './Parent';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from './redux';
const body = document.querySelector('body'),
store = createStore(reducer);
ReactDOM.render(<Provider store={store}><Parent/></Provider>, body);
Parent.js
import React from 'react';
import { connect } from 'react-redux';
import Child from './Child';
class Parent extends React.PureComponent {
get_numbers () {
return this.props.numbers.map((number) => (
<li key={number.id}>
<span>number : </span><br/>
<Child number={number} />
</li>
));
}
render () {
return (
<ul>
{this.get_numbers()}
</ul>
);
}
}
function mapStateToProps(state) {
return { numbers: state.numbers };
}
Parent = connect(mapStateToProps)(Parent);
export default Parent;
Child.js
import React from 'react';
import { connect } from 'react-redux';
import { increase_number } from './redux';
class Child extends React.PureComponent {
render() {
return (
<span onClick={() => this.props.increase_number(this.props.number)}>{this.props.number.value}</span>
);
}
}
function mapDispatchToProps(dispatch) {
return {
increase_number: (number) => dispatch(increase_number({ number }))
};
}
Child = connect(undefined, mapDispatchToProps)(Child);
export default Child;
redux.js
import { createAction, handleActions } from 'redux-actions';
export const increase_number = createAction('increase_number');
const initial_state = {
numbers: [
{ id: 1, value: 1 },
{ id: 2, value: 2 },
{ id: 3, value: 3 }
]
};
export default handleActions({
increase_number: (state, action) => {
// console.log(action.payload.number.value);
action.payload.number.value++;
// console.log(action.payload.number.value);
return { ...state, numbers: [...state.numbers] }
}
}, initial_state);
This is because keys helps you to figure if that element
has changed when it gets compared in the virtual Dom,
so if the id is same for the elements,
ie : 1 for first li, 2 for second li,
the dom will never know since the element updates
based on change in virtual dom , even though the values
are changing
possible solution could be to use id's different from 1, 2, 3 and update the id along with the value so that the Dom is able to figure out the change.
one Possible hack could be
increase_number: (state, action) => {
//console.log(action.payload.number.value);
action.payload.number.value++;
action.payload.number.id--;
//console.log(action.payload.number.value);
return { ...state, numbers: [...state.numbers] }
}
now the key will update everytime with the value but should
not increase since it will be same key of the 2nd li
and the dom will give you an error
so everytime 1 goes to 2 its id will go -- ie 1-1 =0
here is the working Sample
https://codesandbox.io/s/mz6zy5rq28
You need to get a deep copy of numbers array and then increase the value of the number that is passed in payload, like this:
export default handleActions({
increase_number: (state, action) => {
let numberToModify = action.payload.number;
// make a new array with new objects
let newNumbers = state.numbers.map(n => ({ id: n.id, value: n.value }));
// find the one that needs to be modified
let existingNumber = newNumbers.find((number) => (number.id === numberToModify.id));
if (existingNumber) {
existingNumber.value++;
}
return { ...state, numbers: newNumbers };
}
}, initial_state);
Made a working example here.
You need to modify the handleAction as below
export default handleActions({
increase_number: (state, action) => {
action.payload.number.value++
const currentNum = action.payload.number;
let newLst = state.numbers.map((num) => {
return num.id === currentNum.id ? {...action.payload.number} : num
})
return { ...state, numbers: newLst }
}
}, initial_state);
Here is the working sample
https://codesandbox.io/s/4jor55xz5w
Slightly modified you handleActions
export default handleActions({
increase_number: (state, action) => {
action.payload.number.value++;
const currNumber = action.payload.number;
const numbers = state.numbers.map((num) => {
return num.id === currNumber.id ? { ...action.payload.number } : num
});
return { ...state, numbers }
}
}, initial_state);
For increasing the value by one you should make changes in your redux.js file like below:
import { createAction, handleActions } from 'redux-actions';
export const increase_number = createAction('increase_number');
const initial_state = {
numbers: [
{ id: 1, value: 1 },
{ id: 2, value: 2 },
{ id: 3, value: 3 }
]
};
export default handleActions({
increase_number: (state, action) => {
console.log(action.payload.number.value);
// action.payload.numbers.value++;
const numbers = [...state.numbers];
numbers.push({
id: action.payload.number.value++,
value: action.payload.number.value++
})
// console.log(action.payload.number.value);
return { ...state, numbers: numbers }
}
}, initial_state);`

Resources