Reactjs - how to move an element between components onClick of button - javascript

I'm currently new to learning Reactjs and I want to be able to move an element (say, just like a simple header between two components (that I've made as two simple boxes). I got caught up on how to implement the button, I figure I should implement that as its own component since it has important functionality. The point I reached up to is below:
/*class MoveButton extends React.Component {
handleClick() {
alert("Button clicked");
}
render() {
return (
<button onClick={handleClick}>
Click To Move
</button>
);
}
} */
class BoxOne extends React.Component {
render() {
return (
<div className="boxOne-container">
</div>
);
}
}
class BoxTwo extends React.Component {
render() {
return (
<div className="boxTwo-container">
</div>
);
}
}
function App() {
return (
<div>
<BoxOne />
<BoxTwo />
</div>
);
}
ReactDOM.render(<App />,document.getElementById('container'));

here is a sample of a 'moving button'
class MoveButton extends React.Component {
render() {
return (
<button onClick={this.props.onClick}>
Click To Move
</button>
);
}
}
class BoxOne extends React.Component {
render() {
return (
<div className="boxOne-container">
Box1
</div>
);
}
}
class BoxTwo extends React.Component {
render() {
return (
<div className="boxTwo-container">
Box2
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = { pos: 0 }
}
onClick() {
this.setState({ pos: (this.state.pos + 1) % 3 })
}
render () {
return (
<div>
{ this.state.pos === 0 ? <MoveButton onClick={this.onClick.bind(this)}/> : ''}
<BoxOne />
{ this.state.pos === 1 ? <MoveButton onClick={this.onClick.bind(this)}/> : ''}
<BoxTwo />
{ this.state.pos === 2 ? <MoveButton onClick={this.onClick.bind(this)}/> : ''}
</div>
);
}
}
function App() {
return (
<App />
);
}
ReactDOM.render(<App />,document.getElementById('container'));

Related

Run function in child component after action in parent component

I'm using refs for calculating block height in child component, it works fine inside, so after each removeHandler() function "doCalculating" is called
But if I tried to call this into parent component, doCalculating() always return the initial value. Like just after componentDidMount()
Seems like doCalculating() into parent component refers to this.refs.tagList.clientHeight just once and not recalc even after child component update
React version 14.7 is used here, so I cannot use hooks
class ChildComponent extends Component {
componentDidMount() {
this.doCalculating()
}
doCalculating = () => {
const defaultHeight = 50
const newHeight = this.refs.tagList.clientHeight
if (newHeight > defaultHeight ) {
// do logic
}
}
render() {
return (
<ul
ref={"tagList"}
>
{array.map((item, index) => (
<li key={index}>
<button>
{item}
<span onClick={
(e) => {
this.removeHandler()
this.doCalculating()
}
} ></span>
</button>
</li>
)
)}
</ul>
)
}
}
class ParentComponent extends Component {
actionFunc = () => {
// some logic
// call recalculate function, that always return initial value
this.responsesTags.doCalculating()
}
render() {
return (
<div>
<ChildComponent
ref={instance => { this.responsesTags = instance }}
/>
<button onClick={() => this.actionFunc()} />
</div>
)
}
}
What is missing to recalculate a function when called in the parent component?
In my opinion your code works correctly, I've fiddle with your example (a little different), maybe it will be useful to you: https://jsfiddle.net/tu7vxfym/ . If I calculate height of the ul from child and parent component it will calculate correctly.
class ChildComponent extends React.Component {
constructor(){
super();
this.doCalculating = this.doCalculating.bind(this);
this.addDiv = this.addDiv.bind(this);
this.state = {
list: [],
height:undefined
}
}
componentDidMount() {
this.doCalculating()
}
doCalculating (){
const defaultHeight = 50
const newHeight = this.refs.tagList.clientHeight;
this.setState(state=>{
return state.height = this.refs.tagList.clientHeight
})
console.log(newHeight)
}
addDiv(){
this.setState(function(state){
return state.list.push(this.refs.tagList.clientHeight)
})
}
render() {
return (
<div>
<ul ref={"tagList"}>
{this.state.list.map((e,i)=>{
return (<li key={i}>{e}</li>)
})}
</ul>
<h1>Calculated height: {this.state.height}</h1>
<button onClick={this.addDiv}>Add list</button>
<button onClick={this.doCalculating}>Child button</button>
</div>
)
}
}
class ParentComponent extends React.Component {
constructor(){
super();
this.actionFunc = this.actionFunc.bind(this)
}
actionFunc(){
this.responsesTags.doCalculating()
}
render() {
return (
<div>
<ChildComponent ref={instance => { this.responsesTags = instance }}/>
<button onClick={this.actionFunc}>Parent button</button>
</div>
)
}
}

Updating Context from a Nested Component is not Working

I am new to react, I am trying to pass theme string and and toggleTheme function from parent to child using Context API in react.I am practicing example from React Doc with little modification https://reactjs.org/docs/context.html
my code is as following:
import React from 'react';
const ThemeContext = React.createContext({
theme: 'light',
toggleTheme: () => {}
})
class MouseTracker2 extends React.Component {
constructor(props) {
super(props);
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === 'dark'
? 'light'
: 'dark',
}));
};
this.state={
theme: 'dark',
toggleTheme: this.toggleTheme
}
}
render() {
return (
<div>
<ThemeContext.Provider value={this.state}>
<Abc />
</ThemeContext.Provider>
</div>
)
}
}
class Abc extends React.Component {
render() {
return (
<div>
<ThemeContext.Consumer>
{({theme,toggleTheme}) => {return(<Def theme={theme} onClick=
{toggleTheme} />)}}
</ThemeContext.Consumer>
</div>
)
}
}
class Def extends React.Component {
render() {
return (
<div>
<p>efgh</p>
<div>{this.props.theme}</div>
</div>
)
}
}
export default MouseTracker2
In above Code, Context is passing string from parent to child properly. However, it is not passing function from parent to child.
Thanks in Advance :)
The toggleTheme function is passed on to Def by the name onClick and hence this.props.toggleTheme is unavailable and can be accessed by this.props.onClick
class MouseTracker2 extends React.Component {
constructor(props) {
super(props);
this.toggleTheme = () => {
this.setState(state => ({
theme: state.theme === "dark" ? "light" : "dark"
}));
};
this.state = {
theme: "dark",
toggleTheme: this.toggleTheme
};
}
render() {
return (
<div>
<ThemeContext.Provider value={this.state}>
<Abc />
</ThemeContext.Provider>
</div>
);
}
}
class Abc extends React.Component {
render() {
return (
<div>
<ThemeContext.Consumer>
{({ theme, toggleTheme }) => {
return <Def theme={theme} onClick={toggleTheme} />;
}}
</ThemeContext.Consumer>
</div>
);
}
}
class Def extends React.Component {
render() {
return (
<div>
<p>efgh</p>
<div>{this.props.theme}</div>
<button onClick={this.props.onClick}>Toggle</button>
</div>
);
}
}
Working Codesandbox

React Handle interaction state between components

I have a simple component who show element onClick:
class MyComponent extends React.Component {
state = {
isVisible : false
}
render() {
const { isVisble } = this.state
return(
<div>
{isVisble ?
<div onClick={() => this.setState({isVisble: false})}>Hide</div> :
<div onClick={() => this.setState({isVisble: true})}>Show</div>}
</div>
)
}
}
I use this component three times in other component :
class MySuperComponent extends React.Component {
render() {
return(
<div>
<MyComponent />
<MyComponent />
<MyComponent />
</div>
)}
}
I need to pass isVisible at false for all other component if one of have isVisible to true
How to do that ?
Thanks
You should have your component controlled, so move isVisble to props and and then assign it from MySuperComponent.
Also pass MyComponent a callback so it can inform the parent if it wants to change the state.
You'd want some data structure to store that states.
https://codepen.io/mazhuravlev/pen/qxRGzE
class MySuperComponent extends React.Component {
constructor(props) {
super(props);
this.state = {children: [true, true, true]};
this.toggle = this.toggle.bind(this);
}
render() {
return (
<div>
{this.state.children.map((v, i) => <MyComponent visible={v} toggle={() => this.toggle(i)}/>)}
</div>
)
}
toggle(index) {
this.setState({children: this.state.children.map((v, i) => i !== index)});
}
}
class MyComponent extends React.Component {
render() {
const text = this.props.visible ? 'visible' : 'hidden';
return (<div onClick={this.props.toggle}>{text}</div>);
}
}
React.render(<MySuperComponent/>, document.getElementById('app'));
You can check your code here, is this what you want.
example

How can I allow only a single instance of a React component to be present?

I am trying to build a functionality in react.
The problem goes like this: I have three components ComponentA, ComponentB, ComponentC. ComponentC can be rendered as a child of either ComponentA or ComponentB.
What I want to achieve is that at any point of time there is only one instance of ComponentC present in the DOM, can be either as a child of ComponentA or ComponentB, both of which are always present.
class ComponentA extends Component {
render() {
return (
<ComponentC />
);
}
}
class ComponentB extends Component {
render() {
return (
<ComponentC />
);
}
}
class ComponentC extends Component {
render() {
return (
<div>This is ComponentC </div>
);
}
}
I tried to build a way to unmount any instance of ComponentC (if present) whenever it is mounted but couldn't succeed.
Implementing a reference counter with hooks does the job:
function Highlander(Component) {
let immortals = 0
return function(props) {
const [onlyOne, thereIsOnlyOne] = React.useState(false)
React.useEffect(() => {
immortals++
if (immortals === 1) {
thereIsOnlyOne(true)
}
() => {
immortals--
}
}, [])
return onlyOne ? <Component { ...props}/> : <React.Fragment></React.Fragment>
}
}
function Test() {
return "test"
}
const OnlyOneTest = Highlander(Test)
ReactDOM.render(<React.Fragment>
<OnlyOneTest />
<OnlyOneTest />
<OnlyOneTest />
</React.Fragment>, document.getElementById("react"));
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
Starring Highlander as a HoC, which "singletons" the composed component (get the Highlander reference ? uhuh).
There are a few ways you can do this. The simplest method that comes to mind is simply to check the DOM if said node exists. If it exists, just render nothing or prevent the state from toggling it.
class MyApp extends React.Component {
constructor() {
super();
this.state = {
showA: false,
showB: false
};
}
toggleA = () => {
if(document.getElementById("component_c") && !this.state.showA) return;
this.setState((prevState) => ({showA: !prevState.showA}));
}
toggleB = () => {
if(document.getElementById("component_c") && !this.state.showB) return;
this.setState((prevState) => ({showB: !prevState.showB}));
}
render() {
return(
<div>
<div>
<button onClick={this.toggleA}>Show/Hide A</button>
<button onClick={this.toggleB}>Show/Hide B</button>
</div>
{this.state.showA && <ComponentA />}
{this.state.showB && <ComponentB />}
</div>
);
}
}
class ComponentA extends React.Component {
render() {
return (
<ComponentC parent="A" />
);
}
}
class ComponentB extends React.Component {
render() {
return (
<ComponentC parent="B" />
);
}
}
class ComponentC extends React.Component {
render() {
return (
<div id="component_c">{"This is ComponentC, rendered from component" + this.props.parent}</div>
);
}
}
ReactDOM.render(<MyApp />, 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>

React toggle component

I have this simple code below. When I press the Toggle Button the component Child should hide/show, but it's not.
Do I have to re-render something?
I don't want to switch in/out a CSS class, just toggle via a button click
import React, {Component} from 'react';
let active = true
const handleClick = () => {
active = !active
}
class Parent extends React.Component {
render() {
return (
<div>
<OtherComponent />
{active && <Child />}
<button type="button" onClick={handleClick}>
Toggle
</button>
</div>
)
}
}
class Child extends React.Component {
render() {
return (
<div>
I am the child
</div>
)
}
}
class OtherComponent extends React.Component {
render() {
return (
<div>
I am the OtherComponent
</div>
)
}
}
You need to get or set it via state:
class Parent extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
active: true,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
active: !this.state.active
});
}
render() {
return (
<div>
<OtherComponent />
{this.state.active && <Child />}
<button type="button" onClick={this.handleClick}>
Toggle
</button>
</div>
)
}
}
Note that with this approach you will re:render the entire parent component (as well as it's children).
Consider using another approach, when you are passing a prop to the child component and it will render itself with content based on this prop (it can render an empty div or something).
There are number of libraries that make this job easy for you, like react-collapse with animations and stuff.
You should only use state and props to manage your app state.
So instead try:
class Parent extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
active: true
};
this.handleClick = this.handleClick.bind(this);
}
const handleClick = () => {
this.setState({active = !this.state.active});
}
render() {
return (
<div>
<OtherComponent />
{this.state.active && <Child />}
<button type="button" onClick={handleClick}>
Toggle
</button>
</div>
);
}
}
Alernatively, you could use forceUpdate() to force a re-render, but this is strongly discouraged:
const handleClick = () => {
active = !active;
this.forceUpdate();
}

Categories