Re-using React Components - reactjs

I've been doing React lessons, but one thing I don't ever see done is reusing components. For example, if I had a button, and wanted to produce a div every time that button was clicked. How would I do it using a React component that's sole purpose is rendering a single div, And that button uses that one component to add additional divs to the page every time it's clicked?

Do you mean, something like this?
The CustomButton stateless component (presentational) just receives props and can be disabled, text can be changed and a callback can be defined. It doesn't have any own state and can be reused throughout your app where you might need a button.
The ButtonSampleApp is a container component that uses the presentional component and supplies it with a callback, and then handles that callback. To add a div in it's rendering. The ButtonSampleApp uses component state to achieve this
const CustomButton = ({ text, callback, isEnabled }) => {
return <button onClick={() => callback()} disabled={!isEnabled} type="button">{ text }</button>;
};
class ButtonSampleApp extends React.Component {
constructor() {
super();
this.state = {
divs: []
};
}
onButtonClicked() {
const { divs } = this.state;
this.setState( { divs: [...divs, { text: divs.length }] });
}
render() {
const { max } = this.props;
const { divs } = this.state;
return (<div>
<h1>Click on button to add a max of { max } divs</h1>
<div>
{ divs && divs.map( ({text}) => <div key={text}>{ text }</div> ) }
</div>
<CustomButton isEnabled={!divs || divs.length < max} text="Add button" callback={() => this.onButtonClicked()} />
</div>);
}
}
ReactDOM.render( <ButtonSampleApp max={10} />, document.querySelector('#container') );
<script id="react" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.js"></script>
<script id="react-dom" src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.js"></script>
<div id="container"></div>

Related

Creating a parent 'workspace' component in ReactJS

Using ReactJS, I am trying to create a common workspace component that will have toolbar buttons and a navigation menu. The idea I have is to re-use this component to wrap all other dynamic components that I render in the app.
Currently, I've created a Toolbar and MenuBar components that I then add to each component in the app as such:
<Toolbar/>
<MenuBar/>
<Vendors/>
This does not feel right, since my aim is to have just one component which would be something like:
<Workspace>
<Vendor/>
</Workspace>
However, I am not sure of how to achieve this and whether this is the right approach.
As to whether or not it is the right approach is subjective, but I can provide insight into one way to make a "wrapper" type component:
// Your workspace wrapper component
class Workspace {
render() {
return (
<div className="workspace">
<div className="workspace__toolbar">
Toolbar goes here
</div>
<div className="workspace__nav">
Navgoes here
</div>
<div className="workspace__content">
{this.props.children}
</div>
</div>
)
}
}
// Using the component to define another one
class MyComponent {
render() {
return (
<Workspace>
This is my workspace content.
</Workspace>
)
}
}
You can also look at HOC's or Higher Order Components to wrap things.
React offer two traditional ways to make your component re useable
1- High-order Components
you can separate the logic in withWorkspace and then give it a component to apply that logic into it.
function withWorkSpace(WrappedComponent, selectData) {
// ...and returns another component...
return class extends React.Component {
render() {
// ... and renders the wrapped component with the fresh data!
// Notice that we pass through any additional props
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
const Component = () => {
const Content = withWorkSpace(<SomeOtherComponent />)
return <Content />
}
2- Render Props
or you can use function props then give the parent state as arguments, just in case you need the parent state in child component.
const Workspace = () => {
state = {}
render() {
return (
<div className="workspace">
<div className="workspace__toolbar">
{this.props.renderTollbar(this.state)}
</div>
<div className="workspace__nav">
{this.props.renderNavigation(this.state)}
</div>
<div className="workspace__content">
{this.props.children(this.state)}
</div>
</div>
)
}
}
const Toolbar = (props) => {
return <div>Toolbar</div>
}
const Navigation = (props) => {
return <div>Toolbar</div>
}
class Component = () => {
return (
<Workspace
renderNavigation={(WorkspaceState) => <Navigation WorkspaceState={WorkspaceState} />}
renderTollbar={(WorkspaceState) => <Toolbar {...WorkspaceState} />}
>
{(WorkspaceState) => <SomeComponentForContent />}
</Workspace>
)
}

How to change buttons inside react-table using if and else statement?

My reactJS web page has this react-table with 2 buttons inside, I want to change those button when a cell column status changes its value.
render() {
const columns = [
{
Header: "Actions",
accessor: "ID",
Cell: ({ value }) => (
<div style={buttonTablestyle}>
<Button
bsStyle="info"
onClick={() => {
if (window.confirm("You are approving this request after you press OK...")) {
this.updateFunctionYES(value);
}
}}
>
Approve
</Button>
<Button
bsStyle="warning"
onClick={() => {
if (window.confirm("You are rejecting this request after you press OK...")) {
this.updateFunctionNO(value);
}
}}
>
Reject
</Button>
</div>
)
}
];
}
Conditional Styling
The most basic way for conditional styling is to use state and style-objects, which will be explained below. Noteworthy alternatives for conditional styling with prettier syntax and better readability are classnames and styled components.
1) define the state
whenever you want to change a style depending on a value this value should be part of the state, since any change in the state triggers a re-render
2) define style outside the render function
when re-rendering you want to access the styles without re-defining them each time.
3) assign style depending on state
you then assign your needed style within a ternary operator, if statement or switch to use it within your html element.
Example
// define styles you want to use depending on state
const green = {
background: 'green'
}
const red = {
background: 'red'
}
class MyComponent extends React.Component {
state = {
toggle: false
}
handleClick() {
// change state on click
this.setState({
toggle: !this.state.toggle
});
}
render() {
// state change triggers re-render
// define style to be used depending on state
let myStyle = this.state.toggle ? green : red;
return <div>
<div style={myStyle}>MyApp</div>
<button onClick={this.handleClick.bind(this)}>click me</button>
</div>;
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('app')
);
<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='app'></div>
Conditional Displaying
If you want to hide/show conditionally you can add display: none to one of the styles or you use the state to only return the jsx you want to display.
class MyComponent extends React.Component {
state = {
toggle: false
}
handleClick() {
// change state on click
this.setState({
toggle: !this.state.toggle
});
}
render() {
// state change triggers re-render
let element1 = <div>element1</div>;
let element2 = <div>element2</div>;
// define object to be used depending on state
let elementToUse = this.state.toggle ? element1 : element2;
return <div>
{elementToUse}
<button onClick={this.handleClick.bind(this)}>click me</button>
</div>;
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('app')
);
<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='app'></div>

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>

React constructor called only once for same component rendered twice

I expected this toggle to work but somehow the constructor of component <A/> is called only once. https://codesandbox.io/s/jvr720mz75
import React, { Component } from "react";
import ReactDOM from "react-dom";
class App extends Component {
state = { toggle: false };
render() {
const { toggle } = this.state;
return (
<div>
{toggle ? <A prop={"A"} /> : <A prop={"B"} />}
<button onClick={() => this.setState({ toggle: !toggle })}>
toggle
</button>
</div>
);
}
}
class A extends Component {
constructor(props) {
super(props);
console.log("INIT");
this.state = { content: props.prop };
}
render() {
const { content } = this.state;
return <div>{content}</div>;
}
}
ReactDOM.render(<App />, document.getElementById("root"));
I already found a workaround https://codesandbox.io/s/0qmnjow1jw.
<div style={{ display: toggle ? "none" : "block" }}>
<A prop={"A"} />
</div>
<div style={{ display: toggle ? "block" : "none" }}>
<A prop={"B"} />
</div>
I want to understand why the above code is not working
In react if you want to render same component multiple times and treat them as different then you need to provide them a unique key. Try the below code.
{toggle ? <A key="A" prop={"A"} /> : <A key="B" prop={"B"} />}
Since that ternary statement renders results in an <A> component in either case, when the <App>'s state updates and changes toggle, React sees that there is still an <A> in the same place as before, but with a different prop prop. When React re-renders it does so by making as few changes as possible. So since this is the same class of element in the same place, React doesn't need to create a new element when toggle changes, only update the props of that <A> element.
Essentially, the line
{toggle ? <A prop="A"/> : <A prop="B"/> }
is equivalent to
<A prop={ toggle ? "A" : "B" }/>
which perhaps more clearly does not need to create a new <A> component, only update the existing one.
The problem then becomes that you set the state.content of the <A> using props.prop in the constructor, so the state.content is never updated. The cleanest way to fix this would be to use props.prop in the render method of the <A> component instead of state.content. So your A class would look like this:
class A extends Component {
render() {
const { prop } = this.props;
return <div>{ prop }</div>;
}
}
If you must take the prop prop and use it in the <A> component's state, you can use componentDidUpdate. Here's an example:
class A extends Component {
constructor(props) {
super(props);
this.state = {content: props.prop};
}
componentDidUpdate(prevProps) {
if (prevProps.prop !== this.props.prop) {
this.setState({content: this.props.prop});
}
}
render() {
const { content } = this.state;
return <div>{ content }</div>
}
}
React will only call the constructor once. That's the expected outcome.
Looks like you're trying to update the state of the component A based on the props.
You could either use the prop directly or use the componentDidUpdate lifecycle method, as Henry suggested. Another way is using the static method getDerivedStateFromProps to update the state based on the prop passed.
static getDerivedStateFromProps(props, state) {
return ({
content: props.prop
});
}

Propagating mouse events to underneath divs in Reactjs

I have a button in a react page.
On top of the page, I have a transparent fixed div that is used in order to register mouse movements.
render() {
return(
<div>
<div class="fixed-transparent"
onMove={(e)=>{console.log('Mouse moved!')}}></div>
<div class="btn"
onClick={()=>{console.log('Button click!')}}>
I'm a button
</div>
</div>
);
}
The problem is that because the fixed div is on top of the page, the button does not respond to the mouse clicks.
How do I propagate the mouse clicks from the fixed div to the ones underneath?
You have a few options. You can have your child component (that's what "underneath components" are called) listen to the mouse events as well or if you have your "transparent" component pass the click event handler down to the child components. Something like this:
import react, { Component } from 'React';
export class ButtonComponent extends Component {
render() {
const { onClick } = this.props;
return <button onClick={onClick}>Click Me!</button>
}
}
export class TransParentComponent extends Component {
onClick = e => {
console.log('handle click here');
}
onMove = e => {
console.log('Mouse moved!');
}
render() {
return(
<div>
<div class="fixed-transparent" onMouseMove={this.onMove}></div>
<ButtonComponent onClick={this.onClick} />
</div>
);
}
}

Resources