Is interation between two components rendered using React.render() possible? - javascript

I have a list of employees in a page. Each employee item has a check box adjacent to it. This was developed using react js. Now for every 5seconds,I need to check and show a number of employees available/unavailable status on the page based up on the check box status of the employees.
<div id="container">
<!-- List of employees will be placed here by react.js -->
</div>
<div id="status-bar">
Can you please tell me how both divs can be interacted with each other as I am rendering them separately:
React.render(<ListItems />, document.getElementById('container'));
React.render(<StatusComponent />, document.getElementById('status-bar'));

I don't believe that the two components can interact with each other without the use of a parent component. I would suggest making a component that wraps the two, something like this (written in ES6 and not at all tested... just whipped up for example):
class EmployeeModule extends React.component {
_countEmployees() {
// Magic to get employee count here..
var count = getEmployeeCount();
this.setState({numEmployees: count});
}
componentDidMount() {
// Count employees every 5 seconds
this.interval = window.setInterval(() => this._countEmployees(), 5000));
}
componentWillUnmount() {
window.clearInterval(this.interval);
}
render() {
return (
<div className="employee-module-wrapper">
<ListItems />
<StatusComponent numEmployess="{this.state.numEmployees}" />
</div>
)
}
}
Effectively what you need to do is have EmployeeModule handle the interaction between both components. You can get the information you need out of the ListItems module, and set it on the state of the parent module. Calling setState inside _countEmployees will trigger a call to the render function, which will set the property numEmployees on the StatusComponent, thus updating your Status module.
Hope this helps

I think the best and easier way to do it - create store. You can use reduxJS to create store easy. You can also include one block in other and provide your data via this.props.

Related

Where to add a function in React classes - before render() vs in render() vs outside of the class

#1 - when to add code on the class?
class WrappedHorizontalLoginForm extends React.Component {
#2 - when to add code on the class?
handleFormSubmit = (event) => {
event.preventDefault();
const user = event.target.elements.user.value;
console.log(user)
};
render() {
# 3 - when to add code in the render?
return (<div>
<Form onSubmit={this.handleFormSubmit}>
</Form.Group>
</div>
Can someone kindly point out cases on when to add a function in the class vs when to add code within the render method vs when to add it outside the class?
Are their generic guidelines to follow in terms of functionality or specific code?
handleFormSubmit in the code above can be used as an example to explain.
Any code in the render method will run when the component rerenders due to a change in state or props etc. So say you have a component which shows a table of data from a database and it has some state of which cells are selected etc. You would not want to run the database get query when you select a cell as it is not changing. So for that you would have the database request in something like componentWillMount rather than in the render method.
Generally code in the render method is for things such as defining temporary variables or if and else clauses. You want to write most code in the designated react class methods.
Also writing thread blocking code in the render method will reduce the speed of your site.
There is no need to put code just before the render method execution - you probably want to use the component life-cycle methods instead.
if you wants to keep working with Classes so you can use the componentWillMount that will execute just before the component is mounted into the dom.
if you want to work with Functional Components so as part of React 16.8 there is a new feature that called React Hooks.
You can read more about it here: https://reactjs.org/docs/hooks-intro.html

Alter react component state properly

I'm working at a project in which I have to display graphs.
For displaying graphs I'm using vis.js in particular react-vis-network a implementation for using parts of vis.js in React with its stateful approaches.
Initial nodes and edges are loaded before my component is mounted and are passed as props for an initial state.
I attached two eventHandler one direct to a vis.js (the underlying DOM library) and the other at a decorator (button).
The desired/expected behaviour:
A node is removed by clicking either the node or the corresponding button.
Observed behavior:
Sometimes a node is removed and sometimes a node just disappears for a few ms and is reattached but without a decorator/button.
I already tried to start with an empty state and attaching the nodes,edges in componentDidMount() but I got the same result. I hope you can give me a hint.
BTW: Is the way I use to attach components a/the right way?
Every other help to improve my class is appreciated also
class MyNetwork extends Component {
constructor(props){
super(props);
let componentNodes = [];
for (let node of props.nodes){
componentNodes.push(this.createNode(node));
}
let componentEdges = [];
for (let edge of props.edges){
componentEdges.push(this.createEdge(edge));
}
this.state = {nodes:componentNodes,edges:componentEdges};
["_handleButtonClick"].forEach(name => {
this[name] = this[name].bind(this);
});
}
createNode(node){
const Decorator = props => {
return (
<button
onClick={() =>{this._handleButtonClick(props);}}
>
Click Me
</button>
);
};
node.decorator = Decorator;
return React.createElement(Node,{...node})
}
createEdge(edge){
return React.createElement(Edge,{...edge})
}
addNode(node){
this.setState({
nodes: [...this.state.nodes, this.createNode(node)]
})
}
_handleButtonClick(e) {
if(e){
console.log("clicked node has id:" +e.id);
this.removeNode(e.id);
}
}
onSelectNode(params){
console.log(params);
window.myApp.removeNode(params[0]);
}
removeNode(id) {
let array = [...this.state.nodes]; // make a separate copy of the array
let index = array.findIndex(i => i.props.id === id );
array.splice(index, 1);
this.setState({nodes: array});
}
render() {
return (
<div id='network'>
<Network options={this.props.options} onSelectNode={this.onSelectNode}>
{[this.state.nodes]}
{[this.state.edges]}
</Network>
</div>
);
}
}
export default MyNetwork
Before clicking node 2
After clicking node 2
Update 1
I created a live example at stackblitz which isn't working yet caused by other failures I make and can't find.
The components I use are:
Network
Node
Edge
Edge and Node are extending Module
I reworked my MyNetwork component according to some mistakes xadm mentioned.
Components (espacially dynamic) shouldn't be stored in state.
I implemented two new functions nodes() and edges() // line 15-41*
key prop should be used, too.
key is used now // line 18 + 32*
Passed props cannot be modified, you still have to copy initial data
into state. State is required for updates/rerendering.
line 9*
*line numbers in live example I mentioned above
Update 2
I reworked my code and now the life sample is working.
My hope is that I could use the native vis.js events and use them in MyNetwork or other Components I will write.
I read about using 3rd Party DOM event in this question can't figure out to adapt it for my particular case. Because I don't know how to attach the event handler to . Is this possible to do so I can use the event in other components?
Or should I open another question for this topic?
I see several possibilities of problems here.
<Decorator/> should be defined outside of <MyNetwork /> class. Click handler should be passed as prop.
Components (espacially dynamic) shouldn't be stored in state. Just render them in render or by rendering method (called from render). Use <Node/> components with decorator prop, key prop should be used, too.
Passed props cannot be modified, you still have to copy initial data into state. State is required for updates/rerendering. You probably need to remove edge(-es) while removing node.
Create a working example (on stackblitz?) if a problem won't be resolved.
It sounds like React is re-initializing your component when you are clicking a button. Maybe someone smarter than I am can figure out why that is happening...
But since no one has commented on this yet, one way I have handled these sorts of issues is to take the state management out of the display component. You say you are passing the nodes and edges via props from a parent component. You might consider moving the addNode, removeNode, createEdge, and other methods up to the parent component so that it is maintaining the state of the node/edge structure and your display component <MyNetwork/> is only displaying what it receives as props.
Perhaps this isn't an option in your app, but I generally use Redux to remove the state management from the components all together. I find it reduces situations like this where "who should own the state" isn't always clear.

VueJS XHR inside reusable component

Asking for best practice or suggestion how to do it better:
I have 1 global reusable component <MainMenu> inside that component I'm doing XHR request to get menu items.
So if I place <MainMenu> in header and footer XHR will be sent 2 times.
I can also go with props to get menu items in main parent component and pass menu items to <MainMenu> like:
<MainMenu :items="items">
Bet that means I cant quickly reuse it in another project, I will need pass props to it.
And another way is to use state, thats basically same as props.
What will be best option for such use case?
If you don't want to instantiate a new component, but have your main menu in many places you can use ref="menu" which will allow you to access it's innerHTML or outerHTML. I've created an example here to which you can refer.
<div id="app">
<main-menu ref="menu" />
<div v-html="menuHTML"></div>
</div>
refs aren't reactive so if you used v-html="$refs.menu.$el.outerHTML" it wouldn't work since refs are still undefined when the component is created. In order to display it properly you would have to create a property that keeps main menu's HTML and set it in mounted hook:
data() {
return {
menuHTML: ''
}
},
mounted() {
this.menuHTML = this.$refs.menu.$el.outerHTML;
}
This lets you display the menu multiple times without creating new components but it still doesn't change the fact that it's not reactive.
In the example, menu elements are kept in items array. If the objects in items array were to be changed, those changes would be reflected in the main component, but it's clones would remain unchanged. In the example I add class "red" to items after two seconds pass.
To make it work so that changes are reflected in cloned elements you need to add a watcher that observes the changes in items array and updates menuHTML when any change is noticed:
mounted() {
this.menuHTML = this.$refs.menu.$el.outerHTML;
this.$watch(
() => {
return this.$refs.menu.items
},
(val) => {
this.menuHTML = this.$refs.menu.$el.outerHTML;
}, {
deep: true
}
)
}
You can also watch for changes in any data property with:
this.$refs.menu._data
With this you don't need to pass props to your main menu component nor implement any changes to it, but this solution still requires some additional logic to be implemented in it's parent component.

How to manipulate HTML data that comes from the server using ReactJS

So I have this data that comes from the server:
const userInfo = getUserInfo(); //Returns: <span class="info">His name is Sam</span><span class="info">He is 20 years old</span><span class="info">He is from Spain</span><span class="info">He is fluent in English</span>
Then, there are two react components. The first one, ListInfo, shows all of the user info and if the second component, ShowMore, is added to the page, the ListInfo should have to show only the first two pieces of information.
class ListInfo extends React.Component {
render() {
return (
const info = this.props.info;
<div className="ListInfo">
{info}
<ShowMore items={info} />
</div>
);
}
}
class ShowMore extends React.Component {
constructor(props) {
super(props);
this.state = {
isDisplayingAll: true
};
this.handleDisplaying = this.handleDisplaying.bind(this);
}
componentDidMount() {
this.setState({
isDisplayingAll: false
});
}
handleDisplaying(e) {
}
render() {
return (
<button onClick={this.handleDisplaying}>
{this.state.isDisplayingAll ? 'Less' : 'More'}
</button>
);
}
}
ReactDOM.render(
<ListInfo info={userInfo} />,
document.getElementById('root')
);
So here comes the questions:
Firstly, react documents say that you should add a key property if you're rendering items of lists, and the info that comes from the server is a list. So how could I add a key to them? I mean, do I have to write some code to add a key to each item?
Secondly, I should not change the props that a component gets, so how should I change the style of the DOM elements (the last two ones)?
Thirdly, would elements having class instead of className make a problem in rendering?
I think your best bet would be to set up your backend to serve the data as JSON. Then you can write out the JSX to avoid the problems with syntax differences.
If you are absolutely constrained to HTML for some reason, I've had success using react-magic in the past to translate the HTML into JSX. I think I have a webpack loader lying around here somewhere if you want it.
I believe you have a disconnect between JSX, what React uses, and actual HTML. Even though React looks to be using HTML inside of JS, it's actually using some compiler tricks to turn the HTML into JS statements. This explains your first question and third question.
Take a look at this page for what is actually happening under the hood of React.
https://reactjs.org/docs/react-without-jsx.html
There is a way to use the HTML returned back from the server directly in a component. I would read over the Docs here to see how to do it and what are the pros and cons of doing so.
https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
Normally components expose a style prop that allows you to override the default style. I would look into the docs to see if the components you are wanting to style have a style prop.

React: update component's props from outside of render method without using state

Here is what I'm trying to achieve. I have two React components Product and ProductInfoPanel, shown inside a ProductList component. Product displays selected information about a product, such as product name, and price. When a product is clicked, more details will be shown in the ProductInfoPanel. So I need to pass wah twas clicked to the ProductInfoPanel.
Here is how I currently wire them up together. Each Product gets a click handler passed in, which passes back the product object when invoked, then that is passed into the ProductInfoPanel's props. The ProductList uses state to keep track of what was clicked, so when it changes, it triggers the re-rendering of the info panel.
class ProductList extends React.Component {
render() {
return (
<div>
<div className='content'>
<ul>
{ this.props.products.map((product, index) => {
return (
<li key={index}>
<Product product={product}
clickHandler={this.onProductClicked.bind(this)}/>
</li>
);
})}
</ul>
</div>
<div className='side-panel'>
<ProductInfoPanel product={this.state.selectedProduct} />
</div>
</div>
);
}
onProductClicked(clickedProduct) {
// Use the product object that was clicked, and updates the state.
// This updates the info panel content.
this.setState({ selectedProduct: clickedProduct });
}
}
Here is roughly how the two components are constructed.
class Product extends React.Component {
render() {
// Even though it needs only name and price, it gets the whole product
// object passed in so that it can pass it to the info panel in the
// click handler.
return (
<div onClick={this.onClicked.bind(this)}>
<span>{this.props.product.name}</span>
<span>{this.props.product.price}</span>
</div>
);
}
onClicked(e) {
this.props.clickHandler(this.props.product);
}
}
class ProductInfoPanel extends React.Component {
render() {
// Info panel displays more information about a product.
return (
<ul>
<li>{this.props.product.name}</li>
<li>{this.props.product.price}</li>
<li>{this.props.product.description}</li>
<li>{this.props.product.rating}</li>
<li>{this.props.product.review}</li>
</ul>
);
}
}
This is the best I could come up with, but using state to keep track of what product was clicked still sounds wrong to me. I mean, it's not really a state of a component, is it?
If I could update props of a referenced React component from outside of the render method, then I'd try to pass a reference to a ProductInfoPanel to each Product, so they could do update it in their click handler.
Is there a way to achieve what I want and avoid using state to keep track of what was clicked?
You could use a flux-like library like redux, or an alternative like mobx to remove state management from your component, but my personal feeling is to keep it as simple as possible until you really feel like there will be significant benefit in adding another layer of abstraction into your project.
I used to start off projects using redux by default but then one time I kicked myself as it turned out that the added complexity of introducing a redux implementation turned out to be overkill for what was actually a fairly small and simple project. I don't know if there is a hard line to know when you should shy away from using standard state and introduce another library to manage it for you, but I have learned that it's probably safest to do it the easiest and simplest way first until you genuinely feel there is actual benefit in bring in another dependency.
A few bits of advice on your current code...
You are binding your functions in the properties like so:
<Product product={product} clickHandler={this.onProductClicked.bind(this)}/>
When you call function bind it actually returns a new function instance, therefore React's reconciler will see it as a new prop coming into your component and will therefore always re-render the subcomponent tree. Something to be aware of. As an alternative approach you can do early binding in your constructor like so:
class ProductList extends React.Component {
constructor(props) {
super(props);
this.onProductClicked = this.onProductClicked.bind(this);
}
render() {
...
<li key={index}>
<Product product={product}
clickHandler={this.onProductClicked}/>
</li>
...
}
}
Additionally, where you are providing index as they unique key prop above - you should consider using a unique identifier from your product model (if it's available). That way if you add or remove items from the list React will have more information to know whether or not it should re-render all of the Product component instances.
For example:
render() {
...
{
this.props.products.map((product) =>
<li key={product.id}>
<Product product={product}
clickHandler={this.onProductClicked}/>
</li>
)
}
...
}
Read more about these concepts here:
https://facebook.github.io/react/docs/advanced-performance.html
I think it's fine. If there were more components that responded to changes in SelectedProduct, then the value of having the parent component control the state would be more apparent. In your case, it might not seem necessary, since only a single component changes.
However, if your Product also responded by highlighting the SelectedProduct, and a RecentlyViewedProducts list responded in some way to the SelectedProduct, then it would become evident that the SelectedProduct isn't the state of the ProductInfoPanel, but state of a higher level part of the application that it's an observer of.

Categories

Resources