How to pass an Array and access it within React Lifecycle - javascript

I'm using Mongo/Meteor 1.3/React. In my simple example I use an wrapper React component to query the Mongo collection and create an Array. When passing to the Child component, it seems like the Array object is not ready when constructor is called - meaning I can't access the props.
This feels like it must be a common problem. Should I be using a different React Lifecycle Component? Or adding some form of waitOn function? Any advice appreciated!!
Parent Component
export default class BulkMapWrapper extends TrackerReact(React.Component) {
constructor() {
super();
const subscription = Meteor.subscribe("listing",{sort: {_id:-1}})
this.state = {
eventsData: subscription
}
}
render () {
var markerArray = []
markerArray = ...
return(
<div className="panel panel-default">
<div className="panel-body">
<FourthMap
mapParams = {manyEvents}
markers = {markerArray}
/>
</div>
</div>
)
Child Component
export default class GooleMapComponent extends Component {
constructor(props){
super(props)
console.log(this.props.markers);

You should use the componentDidMount function to get the data and then set a new state with the resulting data.
class GetData extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
const subscription = Meteor.subscribe("listing",{sort: {_id:-1}});
this.setState({
eventsData: subscription
});
}
}
You can then pass down the state from the GetData component as props to its children or explicitly to another component in the render function.
This is generally how you should handle AJAX requests in React but I'm not sure if this will translate well to use in Meteor.

Related

React Refs are null after rerender

I'm new to React and I have the following react components that I'm using in a Blazor WASM App.
// Parent
export class Parent extends React.Component{
constructor(props){
super(props);
this.childRef = React.createRef();
// saving reference to component to access it using Blazor JS Interop
window.canvasComponentRef = this
}
render(){
return <Child ref={this.childRef} />
}
parentFoo = () => {
this.childRef.current.foo();
}
}
// Child
export class Child extends React.Component{
constructor(props){
super(props);
}
render(){
return <div> Content </div>
}
foo(){
// some actions in child
}
}
I render the component using...
ReactDOM.render(Parent, document.getElementById('root'));
Result: childRef.current work
When the user navigates away from the Parent component page, I unmount it manually using...
ReactDOM.unmountComponentAtNode(document.getElementById('root'));
When the user comes back to the Parent component page, I render it again using...
ReactDOM.render(Parent, document.getElementById('root'));
Now, when I call window.canvasComponentRef.parentFoo(), childRef.current is null.
Can anyone explain why?
Thank you!
My issue was actually the global variable at
// saving reference to component to access it using Blazor JS Interop
window.canvasComponentRef = this
After refactoring it to get a ref to the Parent component using callback refs as below, the issue got resolved.
let parentRef = null;
function handleRef(element){
parentRef = element;
}
function renderParent(){
const parent = <Parent ref={this.handleRef}/>
ReactDOM.render(parent, document.getElementById('root'));
}
// Now call parent method like below:
function callParentFoo(){
parentRef.parentFoo();
}

How to set variable from parent inside child's componentDidMount react?

I am trying fetch the information from Api request in child component. The problem is that I have faced accessing parent's props. More precisely how to get parent's props and set it inside componentDidMount()?
Parent component
class Parent extends Component {
constructor(){
super()
}
...
<Child id={id}/>
...
export default Parent;
Child component
class Child extends Component {
constructor(){
super()
this.state = {
id:'',
}
}
// I need somehow set parent's "id" inside the url
componentDidMount() {
const url = `https://api.../${id}?api_key=${apiKey}`;
axios.get( url )
...
};
render(){
const { id } = this.props;
console.log('Child id ' + id) // here I see all ids
return(
<div className="item" key={id}>
<p>{id}</p> // here as well
</div>
)
}
}
Child.propTypes = {
item: PropTypes.object
}
export default Child;
Maybe I am looking for in the wrong place and I need just change logic.
I will be grateful for any advice
To access props from anywhere in your Child component you need to pass props to your constructor, then you just access it using this.props.
This should work:
class Child extends Component {
constructor(props){
super(props)
this.state = {},
}
componentDidMount() {
const id = this.props.id
const url = "https://api.../$" + id + "?api_key=${apiKey}";
axios.get( url )
...
};
}
If you have a constructor in a component the props should always be passed to the constructor and also to React component using super()
constructor(props){
super(props)
}
In your case if you have to use your parents props you should also pass the parents props to child also and with same syntax in constructor you can use props that you passed in anywhere of the child component and you can set that props in componentDidMount as well

ReactJS: Updating parent application state from child component

Currently trying to learn React by making a simple application that grabs data from the openFEC API.
I currently have two components defined in my application, a SearchBar and a Candidate component. This is what my App.js currently looks like:
class App extends Component {
constructor(props){
super(props);
this.state = { candidate: [], searchTerm: '' }
}
render() {
return (
<div className="App">
<SearchBar />
<Candidate candidate={this.state.candidate}/>
</div>
);
}
}
export default App;
Problem: I need to update the Candidate component based on the data I receive from the API response. However, I'm making the API call in the SearchBar component and have no way of updating the candidate state defined in the App component.
Should I make the API call in the App component instead? If not, is there a way to send the data I get back from the SearchBar component into my App component?
I think the best way to do this is have the API call in your App Component, and pass that function down as a prop to your SearchBar Component. Your parent component (in this case, App) should be holding on to all of the relevant information and passing down to it's children what they need.
It should look something like this:
class App extends Component {
...
handleSearch(term) {
//handle fetch here
.then(res => this.setState({candidate: res})
}
render() {
<div className="App">
<SearchBar handleSearch={this.handleSearch}/>
<Candidate candidate={this.state.candidate}/>
</div>
}
}
In this way, you can achieve this
class App extends Component {
constructor(props){
super(props);
this.state = { candidate: [], searchTerm: '' }
this.triggerSearch=this.triggerSearch.bind(this);
}
triggerSearch(searchTerm){
this.setState({searchTerm})
}
render() {
return (
<div className="App">
<SearchBar trigerSearch=
{(searchTerm)=>this.triggerSearch(searchTerm)} />
<Candidate candidate={this.state.candidate}/>
</div>
);
}
}
export default App;
You can achieve it this way (without making API call from App).
class App extends Component {
constructor(props){
super(props);
this.state = { candidate: [], searchTerm: '' }
this.onDataReceived = this.onDataReceived.bind(this);
}
onDataReceived(data){
this.setState({ candidate: data });
}
render() {
return (
<div className="App">
<SearchBar onDataReceived={this.onDataReceived}/>
<Candidate candidate={this.state.candidate}/>
</div>
);
}
}
Roughly what happens here is:
You can see how I passed a function as a props to the SearchBar component via onDataReceived props.
You can invoke that function from within SearchBar component (e.g. make API call and call function passed as props with API results).
Invoking onDataReceived function will trigger setState
Calling setState will call render and now the Candidate component will receive more recent data from state.
More.

Redux: passing information to/from window child

Essentially I am trying to create an app where it has a note-pad feature that opens up a window child and passes some information from the parent (which holds the redux state) to it.
However, I am having trouble on how to send the information from the child to the parent, specifically dealing with dispatching action.
I was able to figure it out on passing from parent to child as so without using Redux:
Parent Window
class NavBar extends React.Component {
constructor() {
super()
this.handleNotesMenu = this.handleNotesMenu.bind(this)
}
handleNotesMenu() {
window.id = this.props.id
window.userName = this.props.userName
window.currentReduxState = store.getState()
const notePad = window.open('NotePad', 'notes', 'toolbar=0,status=0,width=715,height=325')
}
Child Window
export class NotePad extends React.Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.state = {
notes: window.opener.state.getIn(['plan', 'notes'])
}
}
handleChange(tabID) {
return e => {
const state = {}
state[tabID] = e.target.value
this.setState(state)
}
}
render() {
return (
<textarea
id="saveArea"
value={this.state.notes}
onChange={this.handleChange('notes')}
name="textarea"
/>
)
}
}
I thought out about that adding action dispatcher to the child was hard, so I was thinking somehow incorporate with sessionStorage. But then I got stumped on how the parent window is able to listen to listenStorage on the fly.
Any thoughts? What would be the best practice when it comes to dealing with window child in React/Redux?
Send a method as prop which will take the returning data as argument from child component and call this method in your childcomponent with that data...like this....
In parent component.....
someMethod(data){
// do something here with data
}
In your render method pass this method to child component as...
<ChildComponent someMethod={this.someMethod} />
Now in your child component call that method like this...
this.props.someMethod(someData);
And that's it your are done...
You have passed the data your parent component without dispatch ... In that someMethod() you can do whatever you wanna do with that data

Reactjs, parent component, state and props

I m actually learning reactjs and I m actually developping a little TODO list, wrapped inside of a "parent component" called TODO.
Inside of this parent, I want to get the current state of the TODO from the concerned store, and then pass this state to child component as property.
The problem is that I dont know where to initialize my parent state values.
In fact, I m using ES6 syntax, and so, I dont have getInitialState() function. It's written in the documentation that I should use component constructor to initialize these state values.
The fact is that if I want to initialize the state inside of my constructor, the this.context (Fluxible Context) is undefined actually.
I decided to move the initialization inside of componentDidMount, but it seems to be an anti pattern, and I need another solution. Can you help me ?
Here's my actual code :
import React from 'react';
import TodoTable from './TodoTable';
import ListStore from '../stores/ListStore';
class Todo extends React.Component {
constructor(props){
super(props);
this.state = {listItem:[]};
this._onStoreChange = this._onStoreChange.bind(this);
}
static contextTypes = {
executeAction: React.PropTypes.func.isRequired,
getStore: React.PropTypes.func.isRequired
};
componentDidMount() {
this.setState(this.getStoreState()); // this is what I need to move inside of the constructor
this.context.getStore(ListStore).addChangeListener(this._onStoreChange);
}
componentWillUnmount() {
this.context.getStore(ListStore).removeChangeListener(this._onStoreChange);
}
_onStoreChange () {
this.setState(this.getStoreState());
}
getStoreState() {
return {
listItem: this.context.getStore(ListStore).getItems() // gives undefined
}
}
add(e){
this.context.executeAction(function (actionContext, payload, done) {
actionContext.dispatch('ADD_ITEM', {name:'toto', key:new Date().getTime()});
});
}
render() {
return (
<div>
<button className='waves-effect waves-light btn' onClick={this.add.bind(this)}>Add</button>
<TodoTable listItems={this.state.listItem}></TodoTable>
</div>
);
}
}
export default Todo;
As a Fluxible user you should benefit from Fluxible addons:
connectToStores.
The following example will listen to changes in FooStore and BarStore and pass foo and bar as props to the Component when it is instantiated.
class Component extends React.Component {
render() {
return (
<ul>
<li>{this.props.foo}</li>
<li>{this.props.bar}</li>
</ul>
);
}
}
Component = connectToStores(Component, [FooStore, BarStore], (context, props) => ({
foo: context.getStore(FooStore).getFoo(),
bar: context.getStore(BarStore).getBar()
}));
export default Component;
Look into fluxible example for more details. Code exсerpt:
var connectToStores = require('fluxible-addons-react/connectToStores');
var TodoStore = require('../stores/TodoStore');
...
TodoApp = connectToStores(TodoApp, [TodoStore], function (context, props) {
return {
items: context.getStore(TodoStore).getAll()
};
});
As a result you wouldn't need to call setState, all store data will be in component's props.

Categories

Resources