How shared state between separate component in React JS? - javascript

I would like to create Dropdown component.
When I click on the DropdownHandler component, I would like to save the state isOpen which can be true or false. If true, this same state is used by <DropdownContent/> to show the content. And by default, the state isOpen is false in <Dropdown /> component
How can I do that please?
App.jsx
import React from 'react';
import {Dropdown, DropdownHandler, DropdownContent} from '../../components/Dropdown/Dropdown.jsx';
class HeaderConnected extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Dropdown>
<DropdownHandler>
<Avatar title="Roberto"></Avatar>
</DropdownHandler>
<DropdownContent>
<li>Menu</li>
<li>Settings</li>
</DropdownContent>
</Dropdown>
</div>
);
}
}
export default HeaderConnected;
Dropdown.jsx
export class Dropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
opened: false
}
}
render() {
return (
<div className="DROPDOWN">
{this.props.children}
</div>
);
}
}
export class DropdownHandler extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="DROPDOWN__HANDLER">
{this.props.children}
</div>
);
}
}
export class DropdownContent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="DROPDOWN__CONTENT">
{this.props.children}
</div>
);
}
}

If you don't want use redux.
You set and control your opend like state of child component and set it to DropdownContent with props. So when you will change opend by click on DropdownHandler, your DropdownContent will recive new propse and call re-render.
import React from 'react';
import {Dropdown, DropdownHandler, DropdownContent} from '../../components/Dropdown/Dropdown.jsx';
class HeaderConnected extends React.Component {
constructor(props) {
super(props);
this.state = {
opened: false
}
}
onClick(){
let condition = this.state.opened;
this.setState({opened: !condition });
}
render() {
return (
<div>
<Dropdown>
<DropdownHandler onClick={this.onClick.bind(this)}>
<Avatar title="Roberto"></Avatar>
</DropdownHandler>
<DropdownContent opened={this.state.opened}>
<li>Menu</li>
<li>Settings</li>
</DropdownContent>
</Dropdown>
</div>
);
}
}
export default HeaderConnected;

I found a solution based on #Andrew answers. Not sure if this is right. But it solves the shared state, only use by Dropdown. I pass the handler and content via props
What do you think?
App.jsx
import React from 'react';
import {Dropdown} from '../../components/Dropdown/Dropdown.jsx';
class HeaderConnected extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Dropdown handler={<Avatar title="Roberto" />}>
<li>Menu</li>
<li>Settings</li>
</Dropdown>
</div>
);
}
}
export default HeaderConnected;
Dropdown.jsx
export class Dropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
opened: false
}
}
render() {
return (
<div className="DROPDOWN">
<div className="DROPDOWN_HANDLER" onClick="...">
{this.props.handler}
</div>
<div className="DROPDOWN_CONTENT">
{this.props.children}
</div>
</div>
);
}
}

Feel free to change the name of the callback "onOpen"
export class Dropdown extends React.Component {
state = {
open: false
}
handleOpen = () => {
this.setState({open: !this.state.open});
}
render() {
return (
<div className="DROPDOWN">
{React.cloneElement(this.props.children, { open: this.state.open, onOpen: this.handleOpen })}
</div>
);
}
}
export class DropdownHandler extends React.Component {
static propTypes = {
open: React.PropTypes.bool.isRequired,
onOpen: React.PropTypes.func.isRequired
}
render() {
return (
<div className="DROPDOWN__HANDLER" onClick={this.props.onOpen}>
{this.props.children}
</div>
);
}
}
export class DropdownContent extends React.Component {
static propTypes = {
open: React.PropTypes.bool.isRequired,
onOpen: React.PropTypes.func.isRequired
}
render() {
// handle visibility here
return (
<div className="DROPDOWN__CONTENT">
{this.props.children}
</div>
);
}
}

Related

TypeError: Class extends value #<Object> is not a constructor or null in react js

I am trying to over ride some function of a prime react button component but i am getting this error while extending prime react button component class.
Here is my code:
import { Button } from "primereact/button";
class BtnBox extends Button {
constructor(props) {
super(props);
this.renderLabel = this.renderLabel.bind(this);
}
renderLabel() {
return <span>icon</span>;
}
}
class IconButton extends React.Component {
render() {
return (
<div className="icon-button">
<BtnBox {...this.props} />
</div>
);
}
}
export default IconButton;
can you try this form ?
import { Button } from "primereact/button";
class BtnBox extends Button {
constructor(props) {
super(props);
}
render = () => <span>{this.props.name}</span>;
}
class IconButton extends React.Component {
render() {
return (
<div className="icon-button">
<BtnBox name='add item' />
</div>
);
}
}
export default IconButton;
if doesn't suite for you, can you share more detail ?

How do I add router links in reactjs

I was following a tutorial where you could take the star wars api and fetch data from the api to show on the website. In the tutorial, they basically show you to make a button and when you click the button, it shows the character info. But I want it to go to another page showing the details of the character using react router. Below is the code
import axios from 'axios';
import './App.css';
import List from './List';
class App extends Component {
constructor(props){
super(props);
this.state={
people: [],
}
this.getPeople=this.getPeople.bind(this);
}
getPeople(){
return axios.get("https://swapi.co/api/people")
.then((response)=>{
console.log(response.data.results);
this.setState({people: response.data.results})
})
}
componentDidMount(){
this.getPeople()
}
render(){
const {people}=this.state;
return (
<div className="App">
<List people={people}/>
</div>
);
}
}
export default App;
List.js
import React, {Component} from 'react';
import CharInfo from './CharInfo';
class List extends Component{
render(){
const people=this.props.people;
return (
<div className="">
{
people.map((p)=>{
console.log(p)
return (
<div key={p.url}>
<h1 className="char-name">{p.name}</h1>
<CharInfo charInfo={p}/>
</div>
)
})
}
</div>
);
}
}
export default List;
CharInfo.js
import React, {Component} from 'react';
class CharInfo extends Component{
constructor(props){
super(props);
this.state={
expanded: false,
}
this.open=this.open.bind(this);
this.close=this.open.bind(this);
}
open(){
this.setState({expanded: !this.state.expanded})
}
close(){
this.setState({expanded: !this.state.expanded})
}
render(){
const info=this.props.charInfo;
if(!this.state.expanded){
return <p className="btn btn-info" onClick={this.open}>Show info</p>
}
return (
<div className="user-details">
<p className="btn btn-danger" onClick={this.close}>Hide Info</p>
<ul>
<li>
<h2>Gender: {info.gender}</h2>
</li>
<li>
<h2>Birth Year: {info.birth_year}</h2>
<li><h2>Hair Color: {info.hair_color}</h2></li>
</li>
</ul>
</div>
)
}
}
export default CharInfo;
in this link, you could see the code in a codesandbox
https://codesandbox.io/s/romantic-pine-lmhvn
You need to integrate the react-router-dom library in order to navigate to different "pages" in your React application.
Working codesandbox: https://codesandbox.io/s/star-wars-api-8bbuf
App.js
import React, { Component } from "react";
import axios from "axios";
import List from "./List";
import Character from "./Character";
import { BrowserRouter, Route } from "react-router-dom";
class App extends Component {
constructor(props) {
super(props);
this.state = {
people: []
};
this.getPeople = this.getPeople.bind(this);
}
getPeople = () => {
axios.get("https://swapi.co/api/people").then(response => {
this.setState({ people: response.data.results });
});
};
componentWillMount() {
this.getPeople();
}
render() {
const { people } = this.state;
console.log(people);
return (
<BrowserRouter>
<Route
path="/"
exact
render={props => <List {...props} people={this.state.people} />}
/>
<Route
path="/char/:charName"
render={props => {
const { charName } = props.match.params;
const foundCharacter = this.state.people.find(
person => person.name.split(" ").join("") == charName
);
return <Character {...props} info={foundCharacter} />;
}}
/>
</BrowserRouter>
);
}
}
export default App;
CharInfo.js
import React, { Component } from "react";
import { Link } from "react-router-dom";
class CharInfo extends Component {
constructor(props) {
super(props);
this.state = {
expanded: false
};
this.open = this.open.bind(this);
this.close = this.open.bind(this);
}
open() {
this.setState({ expanded: !this.state.expanded });
}
close() {
this.setState({ expanded: !this.state.expanded });
}
render() {
const info = this.props.charInfo.name.split(" ").join("");
return (
<div className="user-details">
<Link className="btn btn-info" to={`/char/${info}`}>
Show info
</Link>
</div>
);
}
}
export default CharInfo;
New component: Character.js
const Character = ({ info }) => {
return (
<div>
{
<ul>
<li>
<h2>{info.name}</h2>
</li>
<li>
<h2>Gender: {info.gender}</h2>
</li>
<li>
<h2>Birth Year: {info.birth_year}</h2>
</li>
<li>
<h2>Hair Color: {info.hair_color}</h2>
</li>
{info.vehicles.length > 0 && (
<li>
<h2>Vehicles:</h2>
<ul>
{info.vehicles.map((vehicle, index) => (
<li key={index}>{vehicle}</li>
))}
</ul>
</li>
)}
</ul>
}
</div>
);
};
export default Character;

Accessing variable from different class

How to access the state variable testState from the different class UserAuthentication?
I have tried this without success:
import React from 'react';
import UserAuthenticationUI from './UserAuthentication/UserAuthenticationUI';
class App extends React.Component {
constructor(props) {
super(props);
this.userAuthenticationUI = React.createRef();
this.state={
testState: 'test message'
}
}
render() {
return(
<div>
<UserAuthenticationUI ref={this.userAuthenticationUI} />
<div>
)
}
}
export default App;
How to access this.state.teststate from class UserAuthenticationUI?
import React from "react";
import App from '../App';
class UserAuthenticationUI extends React.Component {
constructor(props) {
super(props);
this.app = React.createRef();
}
render() {
return(
<div>
<App ref={this.app} />
{console.log(this.state.testState)}
</div>
)
}
}
export default UserAuthenticationUI;
You need to pass it via props.
import React from "react";
import UserAuthenticationUI from "./UserAuthentication/UserAuthenticationUI";
class App extends React.Component {
constructor(props) {
super(props);
this.userAuthenticationUI = React.createRef();
this.setParentState = this.setParentState.bind(this);
this.state = {
testState: "test message"
};
}
setParentState(newStateValue){ // this is called from the child component
this.setState({
testState: newStateValue
})
};
render() {
return (
<div>
<UserAuthenticationUI
stateVariable={this.state.testState}
ref={this.userAuthenticationUI}
setParentState={this.setParentState}
/>
</div>
);
}
}
export default App;
UserAuthenticationUI:
import React from "react";
import App from "../App";
class UserAuthenticationUI extends React.Component {
constructor(props) {
super(props);
this.app = React.createRef();
this.onClick = this.onClick.bind(this);
}
onClick(){
const newStateValue = 'new parent state value';
if(typeof this.props.setParentState !== 'undefined'){
this.props.setParentState(newStateValue);
}
}
render() {
const stateProps = this.props.stateVariable;
return (
<div>
<App ref={this.app} />
<div onClick={this.onClick} />
{console.log(stateProps)}
</div>
);
}
}
export default UserAuthenticationUI;
You should think differently.
Try to read the variable via GET methods and set via SET methods.
Do not try to call the variable immediately
Hope this helps.
you can pass it through Props:
import React from 'react';
import UserAuthenticationUI from
'./UserAuthentication/UserAuthenticationUI';
class App extends React.Component {
constructor(props) {
super(props);
this.userAuthenticationUI = React.createRef();
this.state={
testState: 'test message'
}
}
render(){
return(
<div>
<UserAuthenticationUI testState={this.state.testState} />
<div>
)}
}
export default App;
UserAuthenticationUI:
import React from "react";
import App from '../App';
class UserAuthenticationUI extends React.Component
{
constructor(props){
super(props);
}
render(){
return(
<div>
<App/>
{console.log(this.props.testState)}
</div>
)}
}
export default UserAuthenticationUI;
You can access it via props:
<div>
<UserAuthenticationUI testState={this.state.testState} ref={this.userAuthenticationUI} />
<div>
and in UserAuthenticationUI class access it:
<div>
<App ref={this.app} />
{console.log(this.props.testState)}
</div>

ReactJS modal window

I have no idea how to make my app work.
I have a component ContactAdd that onClick must render component ModalWindow. ModalWindow has a parameter isOpened={this.state.open}. How to control this state from parent component?
import React from 'react';
import ContactAddModal from './ContactAddModal.jsx';
import './ContactAdd.css';
export default class ContactAdd extends React.Component {
constructor(props){
super(props);
}
render() {
return (
<div className="add-contact" onClick={ ??????? }>
<img src="./img/add.png" />
<ContactAddModal/>
</div>
)
}
}
import React from 'react';
export default class ContactAddModal extends React.Component {
constructor(props){
super(props);
this.state = {
show: false,
};
this.handleCloseModal = this.handleCloseModal.bind(this);
}
handleCloseModal () {
this.setState({ show: false });
}
render() {
return (
<div className="modal" isOpened={this.state.show}>
<button onClick={this.handleCloseModal}>Close Modal</button>
</div>
)
}
Making the modal a stateless component might be simpler here. The tradeoff being that you would have to handle the closing for every modal, which I think is acceptable. This answer is just an option and by no means an absolute truth.
It could look something like that
export default class ContactAdd extends React.Component {
constructor(props){
super(props);
this.state = {
showModal: true
};
this.hideModal = this.hideModal.bind(this);
}
hideModal() {
this.setState({
showModal: false
});
}
render() {
return (
<div className="add-contact">
<img src="./img/add.png" />
<ContactAddModal handleClose={this.hideModal} isOpened={this.state.showModal} />
</div>
)
}
}
Now the modal:
export default class ContactAddModal extends React.Component {
render() {
return (
<div className="modal" isOpened={this.props.isOpened}>
<button onClick={this.props.handleClose}>Close Modal</button>
</div>
)
}

React JS clock compo

I want to make clock made of components in react. Each value I want to be from different components. I got that code:
import React from 'react';
import ReactDOM from 'react-dom';
class ClockTimeHour extends React.Component{
render(){
return <h1>{this.props.date.getHours()}</h1>
}
}
class ClockTimeMinute extends React.Component{
render(){
return <h1>{this.props.date.getMinutes()}</h1>
}
}
class ClockTimeSecond extends React.Component{
render(){
return <h1>{this.props.date.getSeconds()}</h1>
}
}
class ClockDateYear extends React.Component{
render(){
return <h1>{this.props.date.getFullYear()}</h1>
}
}
class ClockDateMonth extends React.Component{
render(){
return <h1>{this.props.date.getMonth()}</h1>
}
}
class ClockDateDay extends React.Component{
render(){
return <h1>{this.props.date.getDate()}</h1>
}
}
class ClockTime extends React.Component{
render(){
return (
<div>
<ClockTimeHour date={this.state.now}/>
<ClockTimeMinute date={this.state.now}/>
<ClockTimeSecond date={this.state.now}/>
</div>
);
}
}
class ClockDate extends React.Component{
render(){
return (
<div>
<ClockDateYear date={this.state.now}/>
<ClockDateMonth date={this.state.now}/>
<ClockDateDay date={this.state.now}/>
</div>
);
}
}
class Clock extends React.Component{
constructor(props){
super(props)
this.state={
now: new Date()
}
}
componentDidMount() {
this.timerId=setInterval(()=>{
this.setState({ now : new Date() });
},1000);
}
render(){
return (
<div>
<ClockTime date={this.state.now}/>
<ClockDate date={this.state.now}/>
</div>
);
}
}
document.addEventListener('DOMContentLoaded', function(){
ReactDOM.render(
<Clock/>,
document.getElementById('app')
);
});
and when I am running it I got: Uncaught TypeError: Cannot read property 'now' of null at ClockTime.render
You are not setting the state for the component you mentioned, but your are passing through it props as date here <ClockTime date={this.state.now}/>, so I assume you might want to change from:
class ClockTime extends React.Component{
render(){
return <div>
<ClockTimeHour date={this.state.now}/>
<ClockTimeMinute date={this.state.now}/>
<ClockTimeSecond date={this.state.now}/>
</div>
}
}
to:
class ClockTime extends React.Component{
render(){
return (
<div>
<ClockTimeHour date={this.props.date}/>
<ClockTimeMinute date={this.props.date}/>
<ClockTimeSecond date={this.props.date}/>
</div>
);
}
}
You can find here a working example, using your code but applying the changes I've seggested.

Categories