Using Javascript to create html custom Tag - javascript

class Headers extends React.Component {
render() {
const selected = this.props.selectedPane;
const headers = this.props.panes.map((pane, index) => {
const title = pane.title;
const klass = index === selected ? 'active' : '';
return (
<li
key={index}
className={klass}
onClick={() => this.props.onTabChosen(index)}>
{title}{' '}
</li>
);
});
return (
<ul className='tab-header'>
{headers}
</ul>
);
}
}
export default class Tabs extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedPane: 0
};
this.selectTab = this.selectTab.bind(this);
}
selectTab(num) {
this.setState({selectedPane: num});
}
render() {
const pane = this.props.panes[this.state.selectedPane];
return (
<div>
<h1>Tabs</h1>
<div className='tabs'>
<Headers
selectedPane={this.state.selectedPane}
//onTabChosen={this.selectTab}
panes={this.props.panes}>
</Headers>
<div className='tab-content'>
<article>
hellooooo
{pane.content}
</article>
</div>
</div>
</div>
);
}
}
I'm currently creating a 3 tab section where if you click on a tab, it gives you a new pane.
When looking at the render function I see a custom tag called Headers.
I know it coming from the Headers class at the beginning, but how does that format work? Is that a custom tag we building?
Also when looking at its properties such as onTabChosen, when it is deleted in the render method (for learning purposes) and I click on a selected tab, an error comes up saying
"_this.props.onTabChosen is not a function".
this.props.onTabChosen(index).. was written in the Headers class but not as a function correct?
I guess because I am also confused on how this.props.onTabChosen(index) works since onTabChosen was never declared anywhere, just input after props.

When looking at the render function I see a custom tag called "Headers".
That is not a custom tag. That is a React Component.
I know it coming from the Headers class at the beginning, but how does that format work?
Headers is either a function or a class (i.e. a constructor function).
The function will be called and the first argument passed to it will be an object with properties and values that match the props on the JSX element.
If you're going to use React then read a tutorial, this is very introductory level stuff for the framework.
It is covered very early on in both the MDN tutorial and the official React tutorial.
I guess because I am also confused on how this.props.onTabChosen(index) works since onTabChosen was never declared anywhere, just input after props.
It was declared, just not in the piece of code you shared.

Related

Binding setState() to a global function only affects one instance of a component

Note: I have edited the question after the changes I have made according to Nicholas Tower's answer.
I have a global function which bound to a component and changes it's state.
I want to build a form builder system. There is a global function named setLabel which is bound to a component named InputBox and changes it's state. This global function is triggered via another component named ModalPanel which controls the editable properties on the bound component InputBox. I have simplified the function and component class for simplicity of this question.
Here is the global function:
function setLabel(postID, attributeName ){
var text = 'example';
if(text !== ''){
this.setState({ [attributeName] : text});
}
}
And here is the component which is bound to the setLabel function. Notice how setLabel function is passed from parent InputBox component to child ModalPanel component function as a property.
class InputBox extends Component{
constructor(props){
super(props);
this.state = {
placeholder : '',
maxlength: '',
type: '',
}
this.setLabel = setLabel.bind(this); // Binding this to the global function.
}
render(){
let elementId = "inputElement-" + this.props.idCounter;
let mainElement = <Form.Control
id = {elementId}
type = {this.state.type}
placeholder = {this.state.placeholder}
maxLength = {this.state.maxlength}
/>
return <Row>
<ModalPanel
handleHide = {this.props.handleHide}
handleShow = {this.props.handleShow}
setLabel = {this.setLabel}
/>
</Row>
}
}
Lastly, below is the ModalPanel component function where the setLabel function is triggered.
function ModalPanel(props){
return(
......................
......................
......................
<Button variant="primary" onClick = {() => props.setLabel()}>Save changes</Button>
......................
......................
......................)
}
setLabel function which is aimed to set the state of InputBox must be triggered when a button is clicked in the ModalPanel component. The problem is, there are multiple rendered <InputBox /> components on the window and when I try to use this functionality, "the state change" only affect the first instance of <InputBox /> component. What I want to do is that, every instance should have their own internal state and setLabel() function should be bound to the specific component from where it is called. So that, this function can be able to set the state of different component instances. How could I do that?
Addition:
Please check the link below to see a gif image showing how my system works wrong. As you can see, even though I choose the third input box element to edit it's properties (in this case, set it's placeholder text), the change is being made to the first one.
Go to gif
Add a this. to the beginning, as in:
this.setLabel = setLabel.bind(this);
Now you're setting a property on the instance of the InputBox. Make sure to refer to it using this.setLabel when you reference it later in the component.
Is setLabel acting on a specific postID? Is the problem that <Button /> of every <ModalPanel /> acting on the same postID? Because you aren't using setLabel correctly inside <ModalPanel />. setLabel takes in 2 arguments and right now your implementation isn't using any. This is your click handler.
onClick = {() => props.setLabel()}
Try console.logging inside setLabel and see what values you're getting when you click on each button
function setLabel(postID, attributeName){
console.log(postID, attributeName)
var text = 'example';
if(text !== ''){
this.setState({ [attributeName] : text});
}
}
Since the React components only updated from props or state changes, you need to pair the global state with a local state to update the component. See the code below in a sandbox environment.
let value = 0;
function updateStuff () {
console.log("this from update", this.name);
value++;
this.setState({name: "Hakan " + value});
}
class Test extends React.Component {
constructor(props){
super(props);
this.state = {
name: 'notchanged',
counter: 1
}
this.localFunc = this.localFunc.bind(this)
updateStuff = updateStuff.bind(this)
}
localFunc(){
let {counter} = this.state;
this.setState({counter: counter + 1});
updateStuff();
}
render () {
return (
<div>
<div>Test 2</div>;
<div>Counter: {this.state.counter}</div>
<div>Name: {this.state.name}</div>
<button onClick={this.localFunc}>Increment</button>
</div>
);
}
}
ReactDOM.render(
<Test/>,
document.getElementById('root')
);
Think, you are using React in incorrect way
The preferred way for me looks like:
Have a dumb/presentational InputBox which accepts label as a property (in props, not in state)
Have a smart/container component which contains state of multiple InputBoxes and passes the correct label into InputBox
If you are trying to implement InputBox PropertyEditor as a separate component - consider adding event bus, shared between them for example via React Context (or even use full flux/redux concept)
Add this to your function calls after binding them or use arrow functions !

How to return an HTML <div> tag from a javascript function in React?

I am working on a React application where I am trying to render text on the screen when a button is clicked. I have defined a function onButtonClick which gets triggered whenever the button is clicked. However, the HTML that I am returning from the function is not rendered on the screen. I am in the learning stages of React so please excuse me if the question seems silly.
class App extends Component {
constructor() {
super();
this.state = {
blockno:0
}
}
OnButtonClick = () => {
this.setState({blockno: this.state.blockno + 1})
return(
<div>
<h3>Some text</h3>
</div>
);
}
render() {
return(
<div>
<Button onButtonClick={this.OnButtonClick}/>
</div>
);
}
}
The value is being returned, but the framework/browser/etc. has no reason to do anything with that value.
Try thinking about this a different way, a "more React way". You don't want to return the value to be rendered, you want to update state. Something like this:
constructor() {
super();
this.state = {
blockno:0,
showDiv: false // <-- note the new property in state
}
}
OnButtonClick = () => {
this.setState({blockno: this.state.blockno + 1, showDiv: true})
}
Now you're not returning anything, but rather updating the state of the component. Then in your render method you conditionally render the UI based on the current state:
render() {
return(
<div>
<Button onButtonClick={this.OnButtonClick}/>
{
this.state.showDiv
?
<div>
<h3>Some text</h3>
</div>
: ''
}
</div>
);
}
The click handler doesn't modify the page, it just modifies the state of the component you're writing. The render method is responsible for rendering the UI based on that state. Any time state changes, render will be called again to re-render the output.
(Note: It's not 100% clear if this is exactly the functionality you're looking for in the UI, since it's not really clear what you're trying to build. But the point here is to illustrate how to update state and render output in React. Your logic can be tweaked as needed from there.)
You have to make a render based on your state. Please check the tutorial at the react docs to learn more about how React works. It's really good
Here is a version of your code that works. Hope it helps
class App extends Component {
constructor() {
super();
this.state = {
blockno: 0
};
}
OnButtonClick = () => {
//updates the states
this.setState({ blockno: this.state.blockno + 1 });
};
//remember: every time there is an update to the state the render functions re-runs
render() {
//variable holding the blocks in an array
let blocks = []
//if blockno is greater than 0, it checks everytime that there is a state change
if (this.state.blockno > 0) {
//for every block added
for (let index = 0; index < this.state.blockno; index++) {
//We`re going to add to the array of blocks a new div with the block number
blocks.push(
<div>
<h3>My block number is {index}</h3>
</div>
);
}
}
return (
<div>
<div>
{/**button that updates the state on every click */}
<button onClick={this.OnButtonClick}>
Click me to add a new div!
</button>
</div>
{/**This render the blocks variable that holds the divs */}
{blocks}
</div>
);
}
}
What I see is that you are trying to build a counter. The value that you're returning from the click handler function can't be rendered, instead you need to manage it in the render function as follow:
class App extends Component {
constructor() {
super();
this.state = {
blockno: 0
}
}
OnButtonClick = () => {
this.setState(prevState => ({ blockno: prevState.blockno + 1 }));
}
render() {
return(
<div>
{this.state.blockno > 0 && <div>some text {this.state.blockno}</div>}
<Button onButtonClick={this.OnButtonClick} />
</div>
);
}
}
Also note that the setState method is asynchronous, please read the documentation https://reactjs.org/docs/react-component.html#setstate

Set dynamic state name in React.js

I am starting my adventure with React so it is a hard time for me, however I prepared such pen for you to test. Here is a portion of code:
class App extends React.Component {
constructor() {
super();
this.state = {
settings: true,
next: false,
};
}
toggler(abc) {
console.log(">>", abc)
this.setState({
next: !this.state.next
/* {abc}: this.state.{abc} */
})
console.log(this.state.next)
}
render() {
return (
<div className="kalreg">
<MyButton name='settings' isActive={this.state.settings} type="settings" toggle={this.toggler.bind(this)}/>
<MyButton name='settings2' isActive={this.state.settings} type="settings" toggle={this.toggler.bind(this)}/>
<MyButton name='next' isActive={this.state.next} type="next" toggle={this.toggler.bind(this)}/>
</div>)
}
}
class MyButton extends React.Component {
constructor(props) {
super(props);
}
onChangeName(){
console.log(this.props.type)
if ( this.props.isActive ) { console.log("this one is active"); } else { console.log("ouch! it is not active, ignoring!"); return;}
this.props.toggle(this.props.type);
}
render () {
if ( this.props.isActive ) {
return ( <div className="button notVisible" onClick={this.onChangeName.bind(this)}>{this.props.name}</div>)
} else {
return ( <div className="button visible" onClick={this.onChangeName.bind(this)}>{this.props.name}</div>)
}
}
}
ReactDOM.render(<App />, document.getElementById("app"));
What I am trying to achieve is that when i press one of "settings" buttons (yellow) the "next" button becomes unclickable (green). There is a toggle function that every time I click settings button it turns on and off "next" button.
It works quite good, however it is just a draft of bigger project and i want to automate it a little bit.
As you can see I create my <MyButton> with both "isActive" and "type" props. But isActive holds what's inside this.state.settings while type is "settings". Instead of using two variables it would be great to pass only type of button to its component and component, depending on its type would check its parent's this.state.{type}. I used {type} because i would like to check it dynamically. Is that possible?
If so - how to do it?
My first attempt is to pass type from <MyButton> to <App> via toggler function. I named the variable "abc". I commented the way I wanted to do it because it doesn't work:
{abc}: !this.state.{abc}
Any idea to solve this problem would be more than appreciated.
Kalreg.
It is somewhat unclear what you are trying to achieve here. If you want to wire the state dynamically based on type, as you wrote in code: {abc}: !this.state.{abc} each button would toggle itself, not the next button. In this case your syntax is a little incorrect, it will work if you write it like:
[abc]: !this.state[abc]
However as I said, in your example, this makes the settings button change the state for this.state.settings disabling itself instead of the next button.
Another note would be, that if it is not necessary for the MyButton component to know its own type for other reasons, it is unnecessary to pass it as a prop and than make the component pass it back as an argument (this.props.toggle(this.props.type);). You can simply define the toggle function in the parent as:
toggle={() => this.toggler("settings")}
without passing type as a prop.
So basically we want to have the settings and settings2 buttons, and when we click on them, they toggle the state of the next button by making it un-clickable (green).
So if that is our goal, then
we don't need an isActive prop for the settings button. (Because it's always going to be active no matter what)
We also don't need to have a toggle prop on the Next button. (Because clicking the next button isn't supposed to toggle anything)
Instead of having two variables in the state why not just have one and then use that to determine the isActive prop of the next button?
The component would look like this:
constructor() {
super();
this.state = {
nextIsActive: false,
};
}
toggler() {
this.setState({
nextIsActive: !this.state.nextIsActive
})
console.log(this.state);
}
render() {
const {nextIsActive} = this.state
return (
<div className="kalreg">
<MyButton name='settings' isActive={true} type="settings" toggle={this.toggler.bind(this)}/>
<MyButton name='settings2' isActive={true} type="settings" toggle={this.toggler.bind(this)}/>
<MyButton name='next' isActive={nextIsActive}/>
</div>
)
}
That way you don't have to have 2 state properties that you have to dynamically update because it adds more complexity to your application.
You can see the finished product here: Codepen

if else statement in map function reactjs

You don't have to read the whole code, just read the comment in the editQuantity function and in showOrderItem function, specially in the showOrderItem function and my problem is i think just silly, as both of my function are working as they supposed to work,
*editQuantity function supposed to change the state, it changing it, i checked by adding the console line.
*showOrderItem function supposed display the item, he is doing that job as well.
My problem is, i try to add conditional rendering in the showOrderItem function that not working, even though i am able to change the state.
Please read the comment in showOrderItem function, to see where is the problem:
import React from 'react';
export default class ShowOrder extends React.Component{
constructor(props){
super(props);
this.state={
quantityEditing:this.props.orderItems,
}
}
editQuantity(item){
const order=this.state.quantityEditing;
for(var i =0; i<order.length; i++){
if(order[i].item==item){
console.log(order[i].orderQuantityEditing)
order[i].orderQuantityEditing=true;
this.setState({order:this.state.quantityEditing})
console.log(order[i].orderQuantityEditing)
}
}
}
showOrderItem(){
const style = {cursor:'pointer'}
const orderItems=this.state.quantityEditing;
console.log(orderItems)
const orderItem=orderItems.map((item,index)=>
<p>
{orderItems.orderQuantityEditing ? 'some':
<span style={style} onClick={this.editQuantity.bind(this,item.item)}>
//as you can see in here i added conditional rendering, that if orderItems.orderQuantityEditing is true display me some, but that's not working --orderItems.orderQuantityEditing ? 'some'(this part) does not matter how many times i click on property it never display me my string 'some'
{item.quantity}</span>}
<span style={style}> {item.item}</span>
<span style={style}> Q</span>
<span style={style}> N</span>
<span style={style}> X</span>
</p>
);
return orderItem;
}
render(){
return(
<div>
{this.showOrderItem()}
</div>
);
}
}
Instead of
{orderItems.orderQuantityEditing ?
'some'
:
<span style={style} onClick{this.editQuantity.bind(this,item.item)}>
I think you need to write this:
{item.orderQuantityEditing ?
'some'
:
<span style={style} onClick={this.editQuantity.bind(this,item.item)}>
Because you are using map, and item will be each object of array, so you need to use item to access that property. During the for loop, when changing the state you wrote:
order[i].orderQuantityEditing=true;
That it proper, order will be an array and you need to provide the index to access particular object of that.

React/webpack conditionally return require.ensure component (code splitting)

I have a sub component that does not need to be loaded immediately that I want to split out. I am trying to conditionally load in a react component via require.ensure. I am not getting any console errors but I am also not seeing anything being loaded. Here is the code I am calling :
renderContentzones() {
if (this.props.display ) {
return require.ensure([], () => {
const Component = require('./content-zones/component.jsx').default;
return (
<Component
content={this.props.display}
/>
);
});
}
return null;
}
It is just rendering a blank screen currently (no errors). This previously worked when I used import 'displayComponent' from './content-zones/component.jsx' and just returned it like you normally would in react, instead of this require.ensure but. Not sure what I am doing wrong here, any idea how to make something like this work? Thanks!
This is one way to do it, using the state to show the dynamic loaded component:
constructor(){
this.state = {cmp:null};
}
addComponent() {
const ctx = this;
require.ensure(['../ZonesComponent'], function (require) {
const ZonesComponent = require('../ZonesComponent').default;
ctx.setState({cmp:<ZonesComponent />});
});
}
render(){
return (
<div>
<div>Some info</div>
<div><button onClick={this.addComponent.bind(this)}>Add</button></div>
<div>
{this.state.cmp}
</div>
</div>
);
}
When you press the button add the component will be shown.
Hope this help.

Categories