I am new to react was trying to implement CRUD operations in React JS using web API. However, I am receiving an error which I do not understand.
The error is this:
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
▶ 20 stack frames were collapsed.
Module../src/index.js
D:/crud-app/src/index.js:7
4 | import * as serviceWorker from './serviceWorker';
5 | import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
6 | import UserActionApp from './UserCRUD/UserAction';
> 7 | ReactDOM.render(<UserActionApp />, document.getElementById('root'));
8 | serviceWorker.unregister();
9 |
10 | // If you want your app to work offline and load faster, you can change
View compiled
I am using useractionapp component in the file user action.js
Here is the code for index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import UserActionApp from './UserCRUD/UserAction';
ReactDOM.render(<UserActionApp />,
document.getElementById('root'));
serviceWorker.unregister();
Here is the code for User Action:
import React, { Component } from 'react';
import { Container, Button } from 'react-bootstrap';
import UserList from './GetUser';
import AddUser from './AddUser';
import axios from 'axios';
const apiUrl = 'http://localhost:44360/api/User/';
class UserActionApp extends Component {
constructor(props) {
super(props);
this.state = {
isAddUser: false,
error: null,
response: {},
userData: {},
isEdituser: false,
isUserDetails:true,
}
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onCreate() {
this.setState({ isAddUser: true });
this.setState({ isUserDetails: false });
}
onDetails() {
this.setState({ isUserDetails: true });
this.setState({ isAddUser: false });
}
onFormSubmit(data) {
this.setState({ isAddUser: true });
this.setState({ isUserDetails: false });
if (this.state.isEdituser) {
axios.put(apiUrl + 'UpdateEmployeeDetails',data).then(result => {
alert(result.data);
this.setState({
response:result,
isAddUser: false,
isEdituser: false
})
});
} else {
axios.post(apiUrl + 'InsertUserDetails',data).then(result => {
alert(result.data);
this.setState({
response:result,
isAddUser: false,
isEdituser: false
})
});
}
}
editUser = userId => {
this.setState({ isUserDetails: false });
axios.get(apiUrl + "GetUserDetailsById/" + userId).then(result => {
this.setState({
isEdituser: true,
isAddUser: true,
userData: result.data
});
},
(error) => {
this.setState({ error });
}
)
}
render() {
let userForm;
if (this.state.isAddUser || this.state.isEditUser) {
userForm = <AddUser onFormSubmit={this.onFormSubmit} user={this.state.userData} />
}
return (
<div className="App">
<Container>
<h1 style={{ textAlign: 'center' }}>CURD operation in React</h1>
<hr></hr>
{!this.state.isUserDetails && <Button variant="primary" onClick={() => this.onDetails()}> User Details</Button>}
{!this.state.isAddUser && <Button variant="primary" onClick={() => this.onCreate()}>Add User</Button>}
<br></br>
{!this.state.isAddUser && <UserList editUser={this.editUser} />}
{userForm}
</Container>
</div>
);
}
}
export default UserActionApp;
Could you please help out in pointing out the error. Also I am using a different name for the file and the component. Is that causing an issue?
I think your issue comes from here, try this way:
render() {
return (
<div className="App">
<Container>
<h1 style={{ textAlign: 'center' }}>CURD operation in React</h1>
<hr></hr>
{!this.state.isUserDetails && <Button variant="primary" onClick={() => this.onDetails()}> User Details</Button>}
{!this.state.isAddUser && <Button variant="primary" onClick={() => this.onCreate()}>Add User</Button>}
<br></br>
{!this.state.isAddUser && <UserList editUser={this.editUser} />}
{((this.state.isAddUser || this.state.isEditUser) && (
<AddUser onFormSubmit={this.onFormSubmit} user={this.state.userData} />
)) || null}
</Container>
</div>
);
}
}
The reason for this is that if your condition inside render is false, userForm is undefined and react tries to render undefined, running into problems.
Hope this helps.
Related
Context:
I want to trigger an event in a parents child component by an onClick on the parent element
Code:
Parent PlantContainer:
import React from "react";
import ClipLoader from "react-spinners/ClipLoader";
import Box from '#material-ui/core/Box';
import ShowMetric from '../showMetric';
export default class PlantContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
};
}
render() {
return (
<Box>
<h2>{this.props.plantName}</h2>
<ShowMetric
setting={this.props.plantName + ".moisture"}
unit="%">Moisture:</ShowMetric>
<ShowMetric
setting={this.props.plantName + ".conductivity"}
unit="%">Fertility:</ShowMetric>
</Box>
);
}
}
Child ShowMetric:
import React from "react";
import ClipLoader from "react-spinners/ClipLoader";
import resolvePath from 'object-resolve-path';
export default class ShowMetric extends React.Component {
constructor(props) {
super(props);
this.getData = this.getData.bind(this);
this.state = {
isLoading: false,
reading: 0,
};
}
getData() {
this.setState({ isLoading: true });
fetch(URL_HERE, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
},
})
.then(function (response) {
return response.json();
})
.then((json) =>
this.setState({
reading: resolvePath(json, this.props.setting),
isLoading: false,
})
);
}
componentDidMount() {
this.getData();
}
render() {
if (this.state.isLoading) {
return <ClipLoader />;
}
return (
<div onClick={this.getData}>
{this.props.children + " "}
<nobr>{`${this.state.reading.toFixed(1)} ${this.props.unit}`}</nobr>
</div>
);
}
}
Main App.js:
import './App.css';
import React from 'react';
import Container from '#material-ui/core/Container';
import Box from '#material-ui/core/Box';
import PlantContainer from './components/plantContainer';
function App() {
return (
<div className="App">
<Container maxWidth="md">
<Box className="flexBox">
<PlantContainer plantName="Plant_1"/>
<PlantContainer plantName="Plant_2"/>
</Box>
</Container>
</div>
);
}
export default App;
Problem
The above code works as expected, as <ShowMetric/> shows the information and reloads when I click on it.
Now I want to reload all <ShowMetric/> Elements in PlantContainer (maybe trigger the getData() function for each of them) when I click the <H2> Element of PlantContainer.
I tried to find ways how to pass down events or informations to children, but since props can't change at runtime (?) and I don't think a reference would be the best way here, I am a bit at lost on how to implement this.
And as this is my very first react web App and endeavour into this framework please call out any fishy thing you can find in the code.
I think the more elegant way to do this would be to store all the data in the parent component and pass it down to the children through the props.
Here is a possible solution (I used function components as it should be privileged over the class components) :
PlantContainer
function fetchData() {
return fetch(URL_HERE, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
},
})
.then(response => response.json());
}
export default function PlantContainer(props) {
const [data, setData] = React.useState({
isLoading: false,
'moisture': 0,
'conductivity': 0
});
function loadData() {
setData({...data, isLoading: true});
fetchData().then(json => {
setData({
isLoading: false,
'moisture': resolvePath(json, `${props.plantName}.moisture`),
'conductivity': resolvePath(json, `${props.plantName}.conductivity`)
});
});
}
React.useEffect(loadData, []);
return (
<Box>
<h2 onClick={loadData}>{props.plantName}</h2>
{data.isLoading && <ClipLoader/>}
{!data.isLoading && (
<ShowMetric
reading={data['moisture']}
unit="%">Moisture:</ShowMetric>
<ShowMetric
reading={data['conductivity']}
unit="%">Fertility:</ShowMetric>
)}
</Box>
);
}
ShowMetric
export default function ShowMetric(props) {
return (
<div>
{props.children + " "}
<nobr>{`${props.reading.toFixed(1)} ${props.unit}`}</nobr>
</div>
);
}
As you can retrieve all the data by calling the service a single time, it seems to be useless to reload only one metric, so I only give to opportunity to reload both metrics by clicking on the h2 element.
The useImperativeHandle hook is perfect to allow child components and refs.
Fully working example with Typescript support too!:
//Child Component
//Create your ref types here
export type RefHandler = {
pressAlert: () => void;
inputRef: RefObject<HTMLInputElement>;
};
const Child = forwardRef<RefHandler, Props>((props, ref) => {
const submitRef = useRef<HTMLButtonElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
//Initialise your refs here
useImperativeHandle(ref, () => ({
inputRef: inputRef,
pressAlert: () => submitRef?.current?.click()
}));
return (
<div>
<p>Child Component</p>
<input type="text" value="lorem ipsum" ref={inputRef} />
<br />
<button onClick={() => alert("Alert pressed")} ref={submitRef}>
Alert
</button>
</div>
);
});
//Parent
export default function Parent() {
const childRef = useRef<RefHandler>(null);
return (
<>
<p>Parent</p>
<button
onClick={() => {
alert(childRef?.current?.inputRef?.current?.value);
}}
>
Read child input
</button>
<button onClick={() => childRef?.current?.pressAlert()}>
Press child button
</button>
<hr />
<Child ref={childRef} />
</>
);
}
I have this component:
import React, { lazy, Suspense } from 'react';
import { ErrorBoundary } from '../ErrorBoundary';
const FALLBACK = <svg aria-label="" data-testid="icon-fallback" viewBox="0 0 21 21" />;
const ERROR = (
<svg data-testid="icon-notdef" viewBox="0 0 21 21">
<path d="M0.5,0.5v20h20v-20H0.5z M9.1,10.5l-6.6,6.6V3.9L9.1,10.5z M3.9,2.5h13.2l-6.6,6.6L3.9,2.5z M10.5,11.9l6.6,6.6H3.9 L10.5,11.9z M11.9,10.5l6.6-6.6v13.2L11.9,10.5z" />
</svg>
);
export const Icon = ({ ariaLabel, ariaHidden, name, size }) => {
const LazyIcon = lazy(() => import(`../../assets/icons/${size}/${name}.svg`));
return (
<i aria-hidden={ ariaHidden }>
<ErrorBoundary fallback={ ERROR }>
<Suspense fallback={ FALLBACK }>
<LazyIcon aria-label={ ariaLabel } data-testid="icon-module" />
</Suspense>
</ErrorBoundary>
</i>
);
};
I’m trying to test the condition where an SVG is passed in that doesn’t exist, in turn rendering the <ErrorBoundary /> fallback. The ErrorBoundary works in the browser, but not in my test.
This is the failing test:
test('shows notdef icon', async () => {
const { getByTestId } = render(<Icon name='doesnt-exist' />);
const iconModule = await waitFor(() => getByTestId('icon-notdef'));
expect(iconModule).toBeInTheDocument();
});
I get this error message:
TestingLibraryElementError: Unable to find an element by: [data-testid="icon-notdef"]”.
How do I access ErrorBoundary fallback UI in my test?
Edit
This is the code for the <ErrorBoundary /> component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
error: '',
errorInfo: '',
hasError: false,
};
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error({ error, errorInfo });
this.setState({ error, errorInfo });
}
render() {
const { children, fallback } = this.props;
const { error, errorInfo, hasError } = this.state;
// If there is an error AND a fallback UI is passed in…
if (hasError && fallback) {
return fallback;
}
// Otherwise if there is an error with no fallback UI…
if (hasError) {
return (
<details className="error-details">
<summary>There was an error.</summary>
<p style={ { margin: '12px 0 0' } }>{error && error.message}</p>
<pre>
<code>
{errorInfo && errorInfo.componentStack.toString()}
</code>
</pre>
</details>
);
}
// Finally, render the children.
return children;
}
}
ErrorBoundary.propTypes = {
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
fallback: PropTypes.node,
};
… and this is the full error with DOM that I get for the test:
shows notdef icon
TestingLibraryElementError: Unable to find an element by: [data-testid="icon-notdef"]
<body>
<div>
<i
aria-hidden="false"
class="Icon Icon--sm"
>
<span
aria-label=""
data-testid="icon-module"
/>
</i>
</div>
</body>
<html>
<head />
<body>
<div>
<i
aria-hidden="false"
class="Icon Icon--sm"
>
<span
aria-label=""
data-testid="icon-module"
/>
</i>
</div>
</body>
</html>Error: Unable to find an element by: [data-testid="icon-notdef"]
Lastly, my SVG mock:
import React from 'react';
const SvgrMock = React.forwardRef(
function mySVG(props, ref) {
return <span { ...props } ref={ ref } />;
},
);
export const ReactComponent = SvgrMock;
export default SvgrMock;
As discussed in the comments, it is most likely the mock is avoiding the error. Try re mocking the SVG files with a new mock throwing an error.
// tests that require unmocking svg files
describe('non existent svg', () => {
beforeAll(() => {
jest.mock('.svg', () => {
throw new Error('file not found')
});
});
test('shows notdef icon', async () => {
const { getByTestId } = render(<Icon name='doesnt-exist' />);
const iconModule = await waitFor(() => getByTestId('icon-notdef'));
expect(iconModule).toBeInTheDocument();
});
afterAll(() => jest.unmock('.svg'))
})
It is necessary to wrap it to ensure the SVG files are re-mocked only during the test (beforeAll - afterAll) to not interfere with the rest of the tests.
I am trying to run my React application, it compiles successfully (with warnings) but get this error message when I try to click the button.
Here is my App.js:
import React from "react";
import { Component } from "react";
import { Button } from "react-bootstrap";
import Gift from "./Gift.js";
export default class App extends Component {
constructor() {
super();
this.state = { gifts: [] };
}
addGift = () => {
const { gifts } = this.state;
const ids = this.state.gifts.map(gift => gift.id);
const max_id = ids.length > 0 ? Math.max(ids) : 0;
gifts.push({ id: max_id + 1 });
this.setState({ gifts });
};
removeGift = id => {
const gifts = this.state.gifts.filter(gift => gift.id !== id);
this.setState = { ...gifts };
};
render() {
return (
<div>
<h2>Gift Giver</h2>
<div className="gift-list">
{this.state.gifts.map(gift => {
return (
<Gift key={gift.id} gift={gift} removeGift={this.removeGift} />
);
})}
</div>
<Button className="btn-add" onClick={this.addGift}>
Add Gift
</Button>
</div>
);
}
}
The component that I am importing:
import React from "react";
import { Component } from "react";
import {
Form,
FormGroup,
FormControl,
ControlLabel,
Button
} from "react-bootstrap";
export default class Gift extends Component {
constructor() {
super();
this.state = { person: "", present: "" };
}
render() {
return (
<div>
<Form>
<FormGroup>
<ControlLabel>Person</ControlLabel>
<FormControl
onChange={event => this.setState({ person: event.target.value })}
className="input-person"
/>
<ControlLabel>Present</ControlLabel>
<FormControl
onChange={event => this.setState({ present: event.target.value })}
className="input-present"
/>
</FormGroup>
</Form>
<Button
className="btn-remove"
onClick={() => this.props.removeGift(this.props.gift.id)}
>
Remove Gift
</Button>
</div>
);
}
}
I get this warning compilation
Trace: The node type SpreadProperty has been renamed to SpreadElement
at Object.isSpreadProperty (C:\Users\ForAHumanPerson\Desktop\react_tdd\react-quick-start\node_modules#babel\types\lib\validators\generated\index.js:4512:11)
at hasSpread (C:\Users\ForAHumanPerson\Desktop\react_tdd\react-quick-start\node_modules\babel-plugin-transform-object-rest-spread\lib\index.js:38:13)
at PluginPass.ObjectExpression (C:\Users\ForAHumanPerson\Desktop\react_tdd\react-quick-start\node_modules\babel-plugin-transform-object-rest-spread\lib\index.js:234:14)
at newFn (C:\Users\ForAHumanPerson\Desktop\react_tdd\react-quick-start\node_modules#babel\traverse\lib\visitors.js:179:21)
at NodePath._call (C:\Users\ForAHumanPerson\Desktop\react_tdd\react-quick-start\node_modules#babel\traverse\lib\path\context.js:55:20)
at NodePath.call (C:\Users\ForAHumanPerson\Desktop\react_tdd\react-quick-start\node_modules#babel\traverse\lib\path\context.js:42:17)
at NodePath.visit (C:\Users\ForAHumanPerson\Desktop\react_tdd\react-quick-start\node_modules#babel\traverse\lib\path\context.js:90:31)
at TraversalContext.visitQueue (C:\Users\ForAHumanPerson\Desktop\react_tdd\react-quick-start\node_modules#babel\traverse\lib\context.js:112:16)
at TraversalContext.visitSingle (C:\Users\ForAHumanPerson\Desktop\react_tdd\react-quick-start\node_modules#babel\traverse\lib\context.js:84:19)
at TraversalContext.visit (C:\Users\ForAHumanPerson\Desktop\react_tdd\react-quick-start\node_modules#babel\traverse\lib\context.js:140:19)
And also get a similar warning to the Uncaught Error during testing with Jest.
The problem with this line:
removeGift = id => {
const gifts = this.state.gifts.filter(gift => gift.id !== id);
this.setState = { ...gifts }; <---------------
};
this.setState is a method in React.
You have to call it with
this.setState({gifts});
spread operation (...) here is not necessary since gifts is already an array.
I'm trying to implement something really simple, where I want to display a loading between messages. A live example here
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
submitSuccess: false
};
}
onClick = () => {
console.log("click");
this.setState({
isLoading: false,
submitSuccess: true
});
};
render() {
return (
<div className="App">
<button onClick={this.onClick}>click</button>
{this.state.isLoading ? (
<p>loading...</p>
) : this.state.submitSuccess ? (
<p>sucess!</p>
) : (
<p>are you sure?</p>
)}
</div>
);
}
}
What I am trying to do is this example below:
Are you sure?
Loading...
Success!
But I'm not doing this right, since I'm not working properly with the ternary operators. How can I improve this?
Thanks! :)
Fixed here: https://codesandbox.io/s/rln4oz4vwq
Basic idea: Set loading to false by default, because you are not loading anything. On click of button, set loading to true as you truly are. Then on completion of async stuff, set loading to false and submitSuccess to true to indicate you are done.
Code:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
submitSuccess: false
};
}
onClick = () => {
console.log("click");
this.setState({
isLoading: true
});
//lets assume you now do some async stuff and after 2 seconds you are done
setTimeout(() => {
this.setState({
isLoading: false,
submitSuccess: true
});
}, 2000);
};
render() {
return (
<div className="App">
<button onClick={this.onClick}>click</button>
{this.state.isLoading ? (
<p>loading...</p>
) : this.state.submitSuccess ? (
<p>success!</p>
) : (
<p>are you sure?</p>
)}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I have an application with a table, the table has a checkbox to set a Tenant as Active, this variable is a global variable that affects what the user does in other screens of the application.
On the top right of the application, I have another component called ActiveTenant, which basically shows in which tenant the user is working at the moment.
The code of the table component is like this:
import React, { Component } from 'react';
import { Table, Radio} from 'antd';
import { adalApiFetch } from '../../adalConfig';
import Notification from '../../components/notification';
class ListTenants extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
fetchData = () => {
adalApiFetch(fetch, "/Tenant", {})
.then(response => response.json())
.then(responseJson => {
if (!this.isCancelled) {
const results= responseJson.map(row => ({
key: row.id,
TestSiteCollectionUrl: row.TestSiteCollectionUrl,
TenantName: row.TenantName,
Email: row.Email
}))
this.setState({ data: results });
}
})
.catch(error => {
console.error(error);
});
};
componentDidMount(){
this.fetchData();
}
render() {
const columns = [
{
title: 'TenantName',
dataIndex: 'TenantName',
key: 'TenantName',
},
{
title: 'TestSiteCollectionUrl',
dataIndex: 'TestSiteCollectionUrl',
key: 'TestSiteCollectionUrl',
},
{
title: 'Email',
dataIndex: 'Email',
key: 'Email',
}
];
// rowSelection object indicates the need for row selection
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
if(selectedRows[0].TenantName != undefined){
console.log(selectedRows[0].TenantName);
const options = {
method: 'post'
};
adalApiFetch(fetch, "/Tenant/SetTenantActive?TenantName="+selectedRows[0].TenantName.toString(), options)
.then(response =>{
if(response.status === 200){
Notification(
'success',
'Tenant set to active',
''
);
}else{
throw "error";
}
})
.catch(error => {
Notification(
'error',
'Tenant not activated',
error
);
console.error(error);
});
}
},
getCheckboxProps: record => ({
type: Radio
}),
};
return (
<Table rowSelection={rowSelection} columns={columns} dataSource={this.state.data} />
);
}
}
export default ListTenants;
And the code of the ActiveTenant component its also very simple
import React, { Component } from 'react';
import authAction from '../../redux/auth/actions';
import { adalApiFetch } from '../../adalConfig';
class ActiveTenant extends Component {
constructor(props) {
super(props);
this.state = {
tenant: ''
};
}
fetchData = () => {
adalApiFetch(fetch, "/Tenant/GetActiveTenant", {})
.then(response => response.json())
.then(responseJson => {
if (!this.isCancelled) {
this.setState({ tenant: responseJson.TenantName });
}
})
.catch(error => {
this.setState({ tenant: '' });
console.error(error);
});
};
componentDidMount(){
this.fetchData();
}
render() {
return (
<div>You are using tenant: {this.state.tenant }</div>
);
}
}
export default ActiveTenant;
The problem is, if I have multiple tenants on my database registered and I set them to active, the server side action occurs, and the state is changed, however on the top right its still showing the old tenant as active, UNTIL I press F5 to refresh the browser.
How can I achieve this?
For the sake of complete understandment of my code I will need to paste below other components:
TopBar which contains the active tenant
import React, { Component } from "react";
import { connect } from "react-redux";import { Layout } from "antd";
import appActions from "../../redux/app/actions";
import TopbarUser from "./topbarUser";
import TopbarWrapper from "./topbar.style";
import ActiveTenant from "./activetenant";
import TopbarNotification from './topbarNotification';
const { Header } = Layout;
const { toggleCollapsed } = appActions;
class Topbar extends Component {
render() {
const { toggleCollapsed, url, customizedTheme, locale } = this.props;
const collapsed = this.props.collapsed && !this.props.openDrawer;
const styling = {
background: customizedTheme.backgroundColor,
position: 'fixed',
width: '100%',
height: 70
};
return (
<TopbarWrapper>
<Header
style={styling}
className={
collapsed ? "isomorphicTopbar collapsed" : "isomorphicTopbar"
}
>
<div className="isoLeft">
<button
className={
collapsed ? "triggerBtn menuCollapsed" : "triggerBtn menuOpen"
}
style={{ color: customizedTheme.textColor }}
onClick={toggleCollapsed}
/>
</div>
<ul className="isoRight">
<li
onClick={() => this.setState({ selectedItem: 'notification' })}
className="isoNotify"
>
<TopbarNotification locale={locale} />
</li>
<li>
<ActiveTenant />
</li>
<li
onClick={() => this.setState({ selectedItem: "user" })}
className="isoUser"
>
<TopbarUser />
<div>{ process.env.uiversion}</div>
</li>
</ul>
</Header>
</TopbarWrapper>
);
}
}
export default connect(
state => ({
...state.App.toJS(),
locale: state.LanguageSwitcher.toJS().language.locale,
customizedTheme: state.ThemeSwitcher.toJS().topbarTheme
}),
{ toggleCollapsed }
)(Topbar);
App.js which contains the top bar
import React, { Component } from "react";
import { connect } from "react-redux";
import { Layout, LocaleProvider } from "antd";
import { IntlProvider } from "react-intl";
import { Debounce } from "react-throttle";
import WindowResizeListener from "react-window-size-listener";
import { ThemeProvider } from "styled-components";
import authAction from "../../redux/auth/actions";
import appActions from "../../redux/app/actions";
import Sidebar from "../Sidebar/Sidebar";
import Topbar from "../Topbar/Topbar";
import AppRouter from "./AppRouter";
import { siteConfig } from "../../settings";
import themes from "../../settings/themes";
import { themeConfig } from "../../settings";
import AppHolder from "./commonStyle";
import "./global.css";
import { AppLocale } from "../../dashApp";
import ThemeSwitcher from "../../containers/ThemeSwitcher";
const { Content, Footer } = Layout;
const { logout } = authAction;
const { toggleAll } = appActions;
export class App extends Component {
render() {
const { url } = this.props.match;
const { locale, selectedTheme, height } = this.props;
const currentAppLocale = AppLocale[locale];
return (
<LocaleProvider locale={currentAppLocale.antd}>
<IntlProvider
locale={currentAppLocale.locale}
messages={currentAppLocale.messages}
>
<ThemeProvider theme={themes[themeConfig.theme]}>
<AppHolder>
<Layout style={{ height: "100vh" }}>
<Debounce time="1000" handler="onResize">
<WindowResizeListener
onResize={windowSize =>
this.props.toggleAll(
windowSize.windowWidth,
windowSize.windowHeight
)
}
/>
</Debounce>
<Topbar url={url} />
<Layout style={{ flexDirection: "row", overflowX: "hidden" }}>
<Sidebar url={url} />
<Layout
className="isoContentMainLayout"
style={{
height: height
}}
>
<Content
className="isomorphicContent"
style={{
padding: "70px 0 0",
flexShrink: "0",
background: "#f1f3f6",
position: "relative"
}}
>
<AppRouter url={url} />
</Content>
<Footer
style={{
background: "#ffffff",
textAlign: "center",
borderTop: "1px solid #ededed"
}}
>
{siteConfig.footerText}
</Footer>
</Layout>
</Layout>
<ThemeSwitcher />
</Layout>
</AppHolder>
</ThemeProvider>
</IntlProvider>
</LocaleProvider>
);
}
}
export default connect(
state => ({
auth: state.Auth,
locale: state.LanguageSwitcher.toJS().language.locale,
selectedTheme: state.ThemeSwitcher.toJS().changeThemes.themeName,
height: state.App.toJS().height
}),
{ logout, toggleAll }
)(App);
I think this should be enough to illustrate my question.
You're not using redux correctly there. You need to keep somewhere in your redux state your active tenant; That information will be your single source of truth and will be shared among your components throughout the app. Every component that will need that information will be connected to that part of the state and won't need to hold an internal state for that information.
Actions, are where you would call your API to get your active tenant information or set a new tenant as active. These actions wil call reducers that will change your redux state (That includes your active tenant information) and those redux state changes will update your app components.
You should probably take some time to read or re-read the redux doc, it's short and well explained !