React/Meteor init function when data is fully rendered - javascript

I do have the following React Component, which subscribes to MongoDB loads the images from it and returns to the page.
export default class Portfolio extends TrackerReact(React.Component) {
constructor(props) {
super(props);
this.state = {
subscription: {
albumbs: Meteor.subscribe('albums')
}
}
}
componentWillUnmount() {
this.state.subscription.albums.stop();
}
albums() {
return Albums.find().fetch();
}
render() {
function init () {
$('.carousel').flickity({
// options
"lazyLoad": true
});
};
return (
<ReactCSSTransitionGroup component='div' className='carousel' transitionName='postLoad' transitionLeaveTimeout={2000} transitionEnterTimeout={2000}>
{this.albums().map( (album) => {
return <div className='carousel-cell' key={album._id}><AlbumCover albums={album} /></div>
})}
{init()}
</ReactCSSTransitionGroup>
);
}
}
The current init function makes successful init carousel classes, but it looks like the data is loaded faster then the carousel is inited and thought images are nested outside of carousel slider.

Your subscription is not yet ready and your carousel function is triggering before all data is subscribed to, that's why you are having the error.
You have to make sure you are fully subscribed to your collection. Try to create a reactive container, set up a few Session variables and subscribe properly (import container via import {createContainer} from 'meteor/react-meteor-data'):
export default createContainer(() => {
const subscription = Meteor.subscribe('albums');
subscription.ready() ? Session.set("subReady", true) : Session.set("subReady", false);
return{
album: Albums.find().fetch(),
}
}, Portfolio);
then in your render() component:
render(){
if(Session.get("subReady")){
return (
<ReactCSSTransitionGroup component='div' className='carousel' transitionName='postLoad' transitionLeaveTimeout={2000} transitionEnterTimeout={2000}>
{this.albums().map( (album) => {
return <div className='carousel-cell' key={album._id}><AlbumCover albums={album} /></div>
})}
{init()}
</ReactCSSTransitionGroup>
);
}
}
and not to forget, add the carousel code in your componentDidMount() method:
componentDidMount(){
if(Session.get("subReady")){
function init () {
$('.carousel').flickity({
// options
"lazyLoad": true
});
};
}
}
I didn't test this code on my computer, but hopefully it works out for you.
If you want to use your method of subscribing, then make sure that:
1) The subscription is ready.
2) Make sure your carousel code is in your componentDidMount() method and is wrapped in a reactive way to trigger when the data is actually ready/subscribed to.

Related

Component sending undefined via props, because it is mounted before a database query ends

I have a problem related to the asynchronous world with react native.
I need to perform a database query, this query returns me a city vector, this vector should be sent to a picker via props.
The problem is: I perform the query within the ComponentWillUpdate function (by default it's the first function to call before mounting the screen). But even using componentWillMount the component (picker) is being assembled and sending undefined via props.
The data that should be sent is not being processed on time.
The flow of my program is being:
Starts the query (componentWillMount) -> rendering components (render) -> End of Query.
Is it possible to pause the screen mount until the query in ComponentWillUpdate ends?
I tried using async but dont work.
async componentWilldMount() {
try {
await axios.get(`${server}/getdata`)
.then(
//code
})
.catch(function (error) {
//handle error
})
} catch (err) {
// handle error
}
}
It's impossible to pause or stop the rendering the component. What you can do though is to set some property in your state like let's say data. So in your constructor you will have omething like that:
constructor(props) {
this.state = {
data: null
}
}
Then in your componentWillMount or even better componentDidMount you do:
componentDidMount() {
axios.get(`${server}/getdata`)
.then(response => {
this.setState({
data: response
})
)
.catch(function (error) {
//handle error
})
Last step is to render depending on your data so in your render method:
render() {
if(!state.data) {
return null;
}
<SomeComponent data={this.state.data} />
}
Solution : Use isReady flag in the parent component.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isReady: false,
}
}
componentDidMount() {
setTimeout(() => {
this.setState({value: "bbb", isReady: true});
}, 5000)
}
render() {
return (
<div>
{this.state.isReady && <Child value={this.state.value} />}
</div>
);
}
}
class Child extends React.Component {
constructor(props) {
super(props);
console.log(props.value);
}
render() {
return (
<div>{this.props.value}</div>
);
}
}
ReactDOM.render( < App / > , document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Updating React/Redux State from Window function

I have a scenario where I'm trying to update a React/Redux state from a function that's placed on the Window. The function on the window is unable to access the function that's in the React component. Any idea how to bind that function in this kind of setup? This snippet just has a console log where the Redux call would go.
class MyComponent extends Component {
updateRedux = a => {
console.log(a)
}
componentDidMount() {
window.windowFunction = function(a) {
this.updateRedux(a)
}
}
render() {
return (
<Stuff />
)
}
}
this is not accessible inside your function, you need to bind it.
Try with:
class MyComponent extends Component {
updateRedux = a => {
console.log(a)
}
componentDidMount() {
window.windowFunction = function(a) {
this.updateRedux(a)
}.bind(this)
}
render() {
return (
<Stuff />
)
}
}
if you meant that you want to update Redux state with some action (this is the only way to update Redux state by design), then you need to make this action and its functions available to your Component with connect(mapStateToProps, mapDispatchToProps)(Component)
One of the comments above about converting the windowFunction to an arrow function resolved the issue. Thanks!
class MyComponent extends Component {
updateRedux = a => {
console.log(a)
}
componentDidMount() {
window.windowFunction = a => {
this.updateRedux(a)
}.bind(this)
}
render() {
return (
<Stuff />
)
}
}
What you could do is separate the concerns using a presenter and a connected
component, using react-redux. I am assuming you know of this library, comment
if you need more details.
// Simple "presenter", the getComponentData is used to get the data for the
// redux store.
class MyComponentPresenter extends Component {
// returns data for redux
getComponentData () {}
componentDidMount() {
this.props.updateRedux(this); // update Redux
}
render() {
return (
<Stuff />
)
}
}
// This component has the exact same interface, but comes with a updateRedux
// props which automatically dispatches an action
export const MyComponent = connect(null, {
updateRedux(componentInstance) {
return {
type: "updateRedux"
};
}
});
// in the reducer
//
function reducer (state, action) {
switch (action.type) {
case "updateRedux":
return ...
}
}
No more need for globally available function (which in your example is redefined for each instance of MyComponents which is probably not what you want).

isomorphic react.js without flux

I am newbie with react.js . I want to make isomorphic react.js component . I wonder is it possible to make it without flux pattern ? Now I have little component and there is api fetch method inside component and as it seems this api call runs twice :( .
For more clarity, I want to render DOM in server side , and want to handle react.js component events in browser side .
My component looks like :
Class MyComponent extends React.Component{
// my component code
// components events
render() {}
}
if (!is_server()) {
apiFetch.my_api_call(function (result) {
ReactDom.render(<MyComponent data={result.data}/>, document.getElementById('navigation'))
});
}else{
apiFetch.my_api_call(function (result) {
res.status(200).send(
ReactDOMServer.renderToString(React.createElement(MyComponent, {data: result.data}))
);
});
Make a parent Component whose child will be MyComponent
class ParentComponent extends React.Component {
componentDidMount() {
// make api call
apiCall.then((data) => {
this.setState({
reqData : data,
})
})
}
getComponentToRender() {
if(typeof this.state.reqData === 'undefined') {
return false;
} else {
return (
<MyComponent data={result.data}/>
)
}
}
render() {
const componentToRender = this.getComponentToRender();
return (
<div>
<componentToRender />
</div>
)
}
}
Now, render your ParentComponent irrespective of the api call. Once, the ParentComponent is mounted, it will automatically trigger the rendering of MyComponent.

Refresh react component

My node.js server sends with socket.io new data each 10s. In my web application I update this.state each time that my server sends data and force to update with forceUpdate()
However, my react component doesn't refresh, I don't know why. I followed the doc but I missed something...
Parent :
class DataAnalytics extends React.Component {
constructor(props) {
super(props);
socket = this.props.socket;
this.state = {data: []};
socket.on('dataCharts', (res) => {
console.log("new data charts : "+res);
var data = JSON.parse(res);
this.setState({data: data});
this.forceUpdate();
});
}
componentWillUnmount() {
socket.off('dataCharts');
}
render() {
return (
<div id="dataAnalytics">
<Stats data={this.state.data}></Stats>
</div>
);
}
}
export default DataAnalytics;
Child :
class Stats extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="stats" style={{textAlign:'center'}}>
<h4>Number: </h4>
</div>
);
}
componentDidUpdate() {
var data = this.props.data;
if(!jQuery.isEmptyObject(data)) {
$( ".stats" ).html("<h4>Number : data['nb']['counterIn']</h4>");
}
}
}
export default Stats;
Anyone know how to refresh automatically my React component.
The React component doesn't update because it doesn't realize that it's state changes. You can force an update on a React component by creating it each time with a different key attribute.
render() {
return (
<div id="dataAnalytics">
<Stats key={this.uniqueId()} data={this.state.data}></Stats>
</div>
);
}
// Example of a function that generates a unique ID each time
uniqueId: function () {
return new Date().getTime();
}
I usually do it like -
function MyComponent() {
const [_, refresh] = useState()
useEffect(() => {
// Code that's supposed to run on refresh
}, [refresh])
return
<>
{/* Rest of the code */}
<button onclick={() => refresh(true)}>Refresh</button>
</>
}
The idea is to define a state and use it as a dependency of useEffects (or useMemos and useCallbacks).
If there are multiple effect hooks, add refresh to all of them as a dependency.

Update state when view loads

First time working with React, I need to know how to update the state when the view loads.
All I am trying to do is a GET request in order to get a list of dealers for a Casino Game. Basically, I am missing 1 or 2 steps which are for render the dealers's list in the DOM
I will show what I am doing with my code and after that I will explain what I want
here is the first step in the actions part
getDealerActions.js
class GetDealersActions {
constructor () {
this.generateActions('dealerDataSuccess', 'dealerDataFail');
}
getDealers (data) {
const that = this;
that.dispatch();
axios.get('someroute/get-dealers/get-dealers')
.then(function success (response) {
that.actions.dealerDataSuccess({...response.data});
})
}
};
then we move to the stores
getDealersStore.js
class GetDealersStore {
constructor () {
this.state = {
dealerData : null,
};
}
#bind(GetDealersActions.dealerDataSuccess)
dealerDataSuccess (data) {
this.setState({
dealerData : data,
});
console.log(this.state.dealerData);
}
}
in this case that console.log(this.state.dealerData); returns something like this which is exactly what I need
Object {dealersData: Array[3]}
the problems comes in the component part, honestly because I don't know how to handle the data there
class Dealers extends Component {
constructor (props) {
super(props);
}
static getStores () {
return [ GetDealersStore ];
}
static getPropsFromStores () {
return GetDealersStore.getState();
}
render () {
// here need to implement the .map function but
// as this.props.dealerData is null, I am unable to render it.
return (
<div>
<ActionButton onClick={this._getDealers}>Test</ActionButton>
//in some part here I need to render the list of dealers
</div>
);
}
_getDealers = () => {
console.log(this.props.dealerData);
GetDealersActions.getDealers();
}
}
this console.log(this.props.dealerData); returns null the first time, I have to click twice on _getDealers() in order to get this
{params: Object, query: Object, dealerData: Object}
so, what should I do in the render method in order to get dealerData filled out? and what should I do to update the state once I am in the view ?
If you got it, all I need is to get this.props.dealerData with data and not null. So I can render it in this view.
The way this should be structured is:
class Dealers extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
GetDealersActions.getDealers();
}
render() {
let content;
if (this.state.dealerData) {
content = this.state.dealerData.map((datum) => {
return <div>TODO: JSX!</div>;
);
} else {
content = <div>Loading . . .</div>;
}
return <div>{content}</div>;
}
}

Categories