Send to child which component child should render - React js - reactjs

I have a <div> of multiple <input>, and an onClick event to open a modal window, which has to render certain div of inputs for closer zoom, but I am using this modal window for rendering a numpad too.
Is there any way to distinguish which component should be rendered? Is there a possibility to send certain component to modal component(child) and then render this component? I tried something like this -
<Modal update={this.editValue.bind(this)}>{Numpad}</Modal>
or
<Modal child={Numpad} update={this.editValue.bind(this)}/>
and then in Modal(child)
{React.cloneElement(this.props.children, { ...others})}
but it doesn't work, throwing invalid element type error. I can just use switch, inside render component of Modal to distinguish which component to render with props.type, but I want more simply way to do it, any tips?

As far as I understand you correctly, you try to create a tabbed interface within a modal window, so that you could show different content depending on currently active tab. I can see the following solution for that problem.
First, we create the basic component that will hold Modal component and its content:
class App extends React.Component {
render() {
return (
<Modal>
<div>The content of the first tab</div>
<div>The content of the second tab</div>
<div>The content of the third tab</div>
</Modal>
);
}
}
Each child (div) component above represents the content for each tab in Modal.
Now let's create Modal component itself:
class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
tabIndex: 0
};
}
switchTabs(tabIndex) {
this.setState({
tabIndex
});
}
render() {
return (
<div>
<button onClick={this.switchTabs.bind(this, 0)}>1</button>
<button onClick={this.switchTabs.bind(this, 1)}>2</button>
<button onClick={this.switchTabs.bind(this, 2)}>3</button>
<div>
{React.Children.map(this.props.children, (child, index) =>
index === this.state.tabIndex && child
)}
</div>
</div>
)
}
}
We keep the index of active tab (tabIndex) as a part of component's local state. We also show three buttons for switching between these tabs.
Finally, we iterate over components children using React.Children.map and show that child whose index corresponds to current tabIndex.
Try the snippet below.
class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
tabIndex: 0
};
}
switchTabs(tabIndex) {
this.setState({
tabIndex
});
}
render() {
return (
<div>
<button onClick={this.switchTabs.bind(this, 0)}>1</button>
<button onClick={this.switchTabs.bind(this, 1)}>2</button>
<button onClick={this.switchTabs.bind(this, 2)}>3</button>
<div>
{React.Children.map(this.props.children, (child, index) =>
index === this.state.tabIndex && child
)}
</div>
</div>
)
}
}
class App extends React.Component {
render() {
return (
<Modal>
<div>
<img src="https://avatars2.githubusercontent.com/u/4226954?v=3&s=400" height="200" />
</div>
<div style={{ height: '200px', width: '200px', marginTop: '5px' }}>The second tab</div>
<div>
<img src="https://avatars3.githubusercontent.com/u/810671?v=3&s=400" height="200" />
</div>
</Modal>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Related

Add/Remove class to child component div from parent component using refs

I want to add/remove class of child component's outer div from Parent Component on click of sidebar menu button(in parent component).
On click of SidNav toggle button ,expand the nav and add class to the Child Component's outer div so that the content gets pushed to right.
Tried : passing ref as props - I am getting the reference but actual child dom doesnot update when class applied from Parent Component.
Any suggestions would be helpful.
Snippet :
Parent Component :
return(<Child ref={this.ref} navigation={navigation} searchParams={parsedQuery} />)
child component :
function render() {
const { navigation, classlg2, ref } = this.props;
return (
<div className="bx--grid page-content--wrapper">
<div className="bx--row">
<div
ref={ref}
className={
navigation.length > 0
? "bx--col-lg-2 bx--col-lg-14"
: "bx--col-lg-16"
}
>
{routeComponentsElements}
</div>
</div>
</div>
);
}
bx--col-lg-2 is the class that I need to add and remove on toggle of SideNav.
If states are used to handle the className,then the content rerenders..
Example : there is ajax call in child component, when menubutton is clicked it rerenders the page going back to its home page.
Do something like this.
Parent component
this.state = {
childClass: false
}
toggleChildClass = () => {
this.setState(prevState =>({
childClass: !prevstate.childClass
}))
}
render(){
const {childClass} = this.state;
return(
<>
<button onClick={this.toggleChildClass}>Toggle Class</button>
<ChildComponent childClass={childClass} />
</>
)
}
Child Component
render(){
const { childClass } = this.props;
return(
<div
className={childClass ? "bx--col-lg-2 bx--col-lg-14"
: 'bx--col-lg-16'}>
{routeComponentsElements}
</div>
)
}

How to apply MathJax/KaTex to render a React component

I am making a web editor using React & SlateJS. There are LaTex code in the editor content and I want the user to see the rendered LaTex equations. MathJax and KaTex have auto-rendering feature by loading them as CDNs. Once they are loaded, the content on html body is rendered. But they are not live-rendering when I modify the content.
So I have made a button that opens a modal which renders the un-editable edior content in a smaller window, and I want the LaTex codes to be rendered in the modal.
The APP component:
import {Editor} from 'slate-react';
import ReactModel from 'react-modal';
import RenderedEditorDialog from "./RenderedEditorDialog";
class APP extends React.component {
...
render() {
return (
<div className={"editorContainer"}>
<div className={"editor"}>
<Editor
autoFocus
ref={this.ref}
value={this.state.value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
renderMark={this.renderMarks}
renderBlock={this.renderBlock}
/>
</div>
<ReactModal
isOpen={this.state.showMathDialog}
contentLabel="Rendered content"
onRequestClose={this.handleCloseMathDialog}
>
<button onClick={this.handleCloseMathDialog}>Close Dialog</button>
<RenderedEditorDialog value={this.state.value}/>
</ReactModal>
</div>
)
}
}
RenderedEditorDialog (modal) component:
import {Editor} from 'slate-react';
class RenderedEditorDialog extends React.Component {
// eslint-disable-next-line no-useless-constructor
constructor(props) {
super(props);
}
render() {
return (
<div>
<Editor
value={this.props.value}
renderMark={this.renderMarks}
renderBlock={this.renderBlock}/>
</div>
)
}
}
My question is how I can apply MathJax/KaTex to render the content in RenderedEditorDialog component?
Thanks in advance!
KaTeX can be applied to individual DOM elements on demand, instead of all at once, by calling renderMathInElement when desired. Calling this from componentDidUpdate should do the trick:
import {Editor} from 'slate-react';
class RenderedEditorDialog extends React.Component {
constructor(props) {
super(props);
this.ref = React.createRef();
}
render() {
return (
<div ref={this.ref}>
<Editor
value={this.props.value}
renderMark={this.renderMarks}
renderBlock={this.renderBlock}/>
</div>
)
}
componentDidUpdate() {
renderMathInElement(this.ref.current, katexOptions);
}
}
I'm more comfortable with hook-based components instead of classes, which would look like this:
function RenderedEditorDialog(props) {
const ref = useRef();
useEffect(() => {
renderMathInElement(ref.current, katexOptions);
});
return (
<div ref={ref}>
<Editor
value={props.value}
renderMark={props.renderMarks}
renderBlock={props.renderBlock}/>
</div>
)
};
I'm not sure whether you want this on RenderedEditorDialog or another more specific component, but this should give you the idea. For speed, you want to apply renderMathInElement to the smallest container that contains the updated math.

Clicking the button does not work - React

I use a component called "Modal" that I want to make global so I can use it in any other component. Modal will be used in all the components that need it.
My problem is that now the onclick {this.props.stateModal} in Widgets does not work and show nothing.
This is my Widgets.js
class Widgets extends Component {
render(){
return (
<aside className="widgets">
<div id="bq-datos">
<span>Todas tus campañas</span>
<a onClick={this.props.stateModal} className="content-datos orange" data-bq-datos="999"><div>Llamadas <span>ENTRANTES</span></div></a>
<a className="content-datos violet" data-bq-datos="854"><div>Llamadas <span>SALIENTES</span></div></a>
</div>
{
this.props.isModalOpen
? (
<Modal
stateModal = {this.props.stateModal}
isModalOpen={this.props.isModalOpen} >
<ModalWidgets/>
</Modal>
)
: null
}
<Comunicacion/>
</aside>
);
}
}
I need {this.props.stateModal} to work on my Modal component (in Modal.js)
This is my Modal.js with code for {this.props.stateModal} but not works.
import React, { Component } from 'react';
class Modal extends Component {
constructor(props) {
super(props);
this.state = {
isModalOpen: false,
};
this.stateModal = this.stateModal.bind(this);
}
stateModal() {
this.setState({
isModalOpen: !this.state.isModalOpen
});
alert('¡Ohhhh');
}
render(){
if(this.props.isOpen){
return (
<div id="modal">
{this.props.children}
<ModalWidgets/>
</div>
);
} else {
return null;
}
}
}
class ModalWidgets extends Component {
render(){
if(this.props.isModalOpen){
return(
<article id="md-descansos" className="medium">
hola tú!!
</article>
);
}
else{
return(
<div>k pasa!</div>
);
}
}
}
export default Modal;
I think that i need do something in my Modal.js but i don't know what it is
Edit:
I have changed the components to use Modal as the parent of all the other Modal that I want to use, such as ModalWidgets. But now when you click on the button of {this.props.stateModal} in Widgts not works.
Thanks!
You have to use stateModal function somewhere in your Modal component. Something like:
render(){
if(this.props.isOpen){
return (
<ModalGenerico>
<div id="modal">
<button type="button" onClick={this.stateModal}>Click here</button>
{this.props.children}
</div>
</ModalGenerico>
);
} else {
return <ModalGenerico />;
}
}
The button in the example above should be replaced with your backdrop of the modal (or anything else as you like).
Edited: You should take a look at this article State vs Props. Because I notice that you weren't clear the usage of them.
Besides, I don't think there's such thing called global component as you described. Every components in react are reusable and can be imported anywhere in the project.

Change element content with onClick in React

In my application I have multiple blocks generated dynamically and each one of them has an onClick event.
My goal is to be able to change the contents of the div when the click happens.
Is there a way to do this thru event.target property of the onClick event?
Or should i create a ref for each div upon creation and then work with refs?
Or should i create an array of Div elements in component state and search&modify the element later re-rendering all divs from array?
Since blocks are generating dynamically, have onClick event on children components.
const Parent = () => {
return (
<Child content={content} contentAfterClick={content} />
<Child content={content} contentAfterClick={content} />
)
}
class Child extends Component {
constructor() {
super();
this.state ={
read: false,
};
}
render() {
if (this.state.read) {
return(
<div>{this.props.contentAfterClick}</div>
)
}
return (
<div onClick={() => this.setState({ read: true })}>
<div>{this.props.content}</div>
</div>
);
};
}
This demo illustrates how you can change the contents of a div, the text, when a click happens through the onClick and event.target object as you wanted.
You can do this through the use of refs, but normally you want to avoid refs unless absolutely necessary because there are easier ways to accomplish the same thing in React.
Also wouldn't want to keep the physical DOM nodes, HTMLDivElement, in state. Instead, keep the contents it relies upon in state (in our case a single number value), then when you change the contents it will automatically update and rerender our div nodes.
// Example class component
class Container extends React.Component {
constructor(props) {
super(props);
const blocks = [];
blocks.push(0);
blocks.push(0);
blocks.push(0);
this.state = { blocks: blocks, clickedElementContents: "" };
}
increment(event, index) {
const newBlocks = this.state.blocks;
newBlocks[index]++;
this.setState({ blocks: newBlocks, clickedElementContents: event.target.innerText });
}
render() {
return (
<div>
<div className="block" onClick={(event) => { this.increment(event, 0) }}>Click me! ({this.state.blocks[0]})</div>
<div className="block" onClick={(event) => { this.increment(event, 1) }}>Click me! ({this.state.blocks[1]})</div>
<div className="block" onClick={(event) => { this.increment(event, 2) }}>Click me! ({this.state.blocks[2]})</div>
<span>Contents of the clicked element: {this.state.clickedElementContents}</span>
</div>
);
}
}
// Render it
ReactDOM.render(
<Container/>,
document.body
);
.block {
display: inline-block;
background-color: black;
color: white;
padding: 5px;
margin-right: 10px;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Element not being displayed when div is removed in react

I am quite new to react but I am trying to pass element from the Course component to my Coursebox component. I am doing this successfully but the button is being displayed since I am not passing that with this.prompt. I would like to remove the div id="course" from the HTML because I only want the course component to be displayed within the couresebox component.
This is my JSX code
class Course extends React.Component {
render() {
return (
<div className="course">
<h3>{this.props.coursename}</h3>
<h4> {this.props.status} {this.props.progress}</h4>
<button>Start exercise</button>
</div>
);
}
}
ReactDOM.render( < Course />, document.getElementById('course'));
class Coursebox extends React.Component {
render() {
return (
<div>
<Course coursename="Negotiation" progress= "20%" status="Progress"/>
<Course coursename="Frontend" progress="56%" status="Progress"/>
<Course coursename="Food" status="Progress" progress="43%"/>
</div>
);
}
}
ReactDOM.render( < Coursebox />, document.getElementById('coursebox'));
and HTML
<header id="header">
</header>
<h3 id="search"></h3>
<div id="coursebox"></div>
<div id="course"></div>
When I remove the nothing is being displayed on the page apart from the header. Since I am passing the element from Course to the Coursebox component, shouldn't I be able to remove the course div from the HTML?
If it is still needed, why is that?
Is there a way for me to only display the button when a course name is being passed?
Thanks :)
Avoid rendering the course component. Ideally there should be just one render method and all other components should be called from that component. So render on CourseBox component only.
class Course extends React.Component {
render() {
console.log("hey")
return (
<div className="course">
<h3>{this.props.coursename}</h3>
<h4> {this.props.status} {this.props.progress}</h4>
<button>Start exercise</button>
</div>
);
}
}
class Coursebox extends React.Component {
render() {
return (
<div>
<Course coursename="Negotiation" progress= "20%" status="Progress"/>
<Course coursename="Frontend" progress="56%" status="Progress"/>
<Course coursename="Food" status="Progress" progress="43%"/>
</div>
);
}
}
React.render(<Coursebox />, document.getElementById('coursebox'));
Working JS fiddle http://jsfiddle.net/kmbw9wgt/3/
1.) See this line
ReactDOM.render( < Course />, document.getElementById('course'));
You are explicitly asking react to render Course in a div which has id 'course'.
If you remove this, it will not render separately, but only from within Coursebox element.
Then you can safely remove that div.
2.) Yes, you can only show button when course name is passed. Using If condition ternary operator. Add your button in following way:
<h3>{this.props.coursename}</h3>
<h4> {this.props.status} {this.props.progress}</h4>
{ this.props.coursename ? (<button>Start exercise</button>): null}

Resources