Create react functional component with hook and publish him in npm package - react-hooks

I try to create own npm pack.
I created and published my component, it is working, but when I add UseEffect in my component I have errors.
What is goin on?
import React, { FC, useEffect, useState } from 'react';
import './Button.scss';
export interface ButtonProps {
children: any;
styles?: Array<string>;
}
const Button: FC<ButtonProps> = (
{
children,
styles,
...props
}) => {
const [active, setActive] = useState(null);
const root_styles = ['pef_button'];
useEffect(()=>{
console.log('JK:DAHJS:JDKHA:SKJhd');
},[])
if(styles){
for (let i = 0; i < styles.length; i++){
root_styles.push(styles[i]);
}
}
return(
<button {...props} className={root_styles.join(' ')} >
{children}
</button>
);
};
export default Button;
I do import this component in my app and have error
import React, {useContext, useState, useEffect} from 'react';
import {Button, Input} from 'My[![enter image description here][1]][1]-react-library'
const MainPage: React.FunctionComponent = () => {
return (
<div>
<div>
<Button >
zxc
</Button>
</div>
</div>
);
};
export default MainPage;
Maybe I should use component classes instead of functional-components

What are you using to package it? I had the same issue while using Rollup. I solved it by adding react and react-dom to external in rollup.config.js.
export default {
...,
external: [
'react',
'react-dom',
]
}

Related

React-Bootstrap: How to create tooltips with different text?

In the react-bootstap docs, they show an examples of a paragraph with multiple tooltips, but they never show the code they used to achieve it. They do, however, show the code for doing a single, hard-coded tooltip.
In this example, how could I get one hyperlinked word to show “hello” on hover and the other one to show “goodbye” on hover?
import React from "react";
import OverlayTrigger from "react-bootstrap/OverlayTrigger";
import Tooltip from "react-bootstrap/Tooltip";
​
const renderTooltip = (props) => (
<Tooltip id="button-tooltip" {...props}>
hello
</Tooltip>
);
​
class TestTooltip extends React.Component {
render() {
return (
<div>
What if I want a tooltip that says "hello"
<OverlayTrigger placement="bottom" overlay={renderTooltip}> here </OverlayTrigger>
and "goodbye" <OverlayTrigger placement="bottom" overlay={renderTooltip}> here </OverlayTrigger>?
</div>
);
}
}
​
export default TestTooltip;
see this code:
import React from "react";
import Tooltip from 'react-bootstrap/Tooltip';
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
import Button from 'react-bootstrap/Button';
const TooltipButton = (props) => {
if (props.text) {
return (
<OverlayTrigger
placement="right"
delay={{ show: 250, hide: 400 }}
overlay={<Tooltip id="button-tooltip-2">{props.text}</Tooltip>}
>
<Button variant="success">i</Button>
</OverlayTrigger>
);
}
else {
return null;
}
}
export default TooltipButton

Invalid hook call in two-line functional component

Why would this code create 'Invalid hook call'?
react and react-dom are the same version v16.8.6.
Simply calling useState triggers error?
What am I missing?
$ npm ls react react-dom
vehmain#0.1.0 /mnt/g/development/javascript/pow-vehmain
├── react#16.8.6
└── react-dom#16.8.6
Runtime:
Invalid hook call. Hooks can only be called inside of the body of a function component...
ServiceVisitMaintenance
src/components/ServiceVisit/ServiceVisitMaintenance.js:3
1 | import React, { useState } from 'react';
2 |
> 3 | const ServiceVisitMaintenance = () => {
4 | const [vehicleList, setVehicleList] = useState([]);
5 | return <div>Hello</div>;
6 | };
ServiceVisitMaintenance.js:
import React, { useState } from 'react';
const ServiceVisitMaintenance = () => {
const [vehicleList, setVehicleList] = useState([]);
return <div>Hello</div>;
};
export { ServiceVisitMaintenance };
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { App } from './App';
import * as serviceWorker from './serviceWorker';
import './index.css';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();1
App.js
import React from 'react';
import { BaseLayout } from './BaseLayout';
import { BrowserRouter as Router } from 'react-router-dom';
const App = () => (
<Router>
<BaseLayout />
</Router>
);
export { App };
BaseLayout.js
import React from 'react';
import { Route, Link, Switch } from 'react-router-dom';
import './App.css';
import { ServiceVisitMaintenance } from './components/ServiceVisit/ServiceVisitMaintenance';
const BaseLayout = () => (
<div>
<aside>
<ul>
<li>
<Link to='/ServiceVisitMaintenance'>Service Visit</Link>
</li>
</ul>
</aside>
<article>
<Switch>
<Route path='/ServiceVisitMaintenance' exact render={ServiceVisitMaintenance} />
</Switch>
</article>
</div>
);
export { BaseLayout };
Change your Route props from render to component
<Route path='/ServiceVisitMaintenance' exact component={ServiceVisitMaintenance} />
Or make render a function that returns the component
<Route path='/ServiceVisitMaintenance' exact render={() => <ServiceVisitMaintenance />} />

Redux state changed but component props is not updating

I am using redux to control an Ant Design Modal component with a boolean state. Basically it has a button that dispatch action to change the state, and the component will read the state value.
The state is changed properly but the component props value is not updating accordingly. Not sure why it is not working.
I have tried different approaches in reducer like creating a new boolean object to avoid mutating the state but no luck.
myAction.js
export const modalVisibilityOn = () => ({
type: 'MODAL_ON'
})
export const modalVisibilityOff = () => ({
type: 'MODAL_OFF'
})
myReducer.js
const modalVisibility = (state = false, action) => {
switch (action.type){
case 'MODAL_ON':
return true
case 'MODAL_OFF':
return false
default:
return state
}
}
export default modalVisibility
myRootReducer.js
import { combineReducers } from 'redux'
import modalVisibility from './signPage/myReducer'
export default combineReducers({
modalVisibility
})
myModal.js
import React from "react";
import PropTypes from 'prop-types'
import { Modal, Input } from 'antd';
import { connect } from 'react-redux'
import { modalVisibilityOff } from '../../reducers/signPage/myAction'
class myModal extends React.Component {
render() {
const { visibility, handleOk, handleCancel } = this.props;
myModal.propTypes = {
visibility: PropTypes.bool.isRequired,
handleOk: PropTypes.func.isRequired,
handleCancel: PropTypes.func.isRequired,
}
return (
<Modal
title="Sign"
visible={visibility}
onOk={handleOk}
onCancel={handleCancel}
closable={false}
>
<p>Please input your staff ID</p>
<Input addonBefore="Staff ID" />
</Modal>
);
}
}
const mapStateToProps = state => {
return {
visibility: state.modalVisibility
}
}
const mapDispatchToProps = dispatch => ({
handleOk: () => dispatch(modalVisibilityOff()),
handleCancel: () => dispatch(modalVisibilityOff()),
})
export default connect(
mapStateToProps,mapDispatchToProps
)(myModal)
myModalContainer.js
import React from "react";
import { Input } from "antd";
import { Button } from 'antd';
import { Row, Col } from 'antd';
import { Typography } from 'antd';
import PropTypes from 'prop-types'
import myModal from '../../dialogs/signPage/myModal';
import { connect } from 'react-redux'
import { modalVisibilityOn } from '../../reducers/signPage/myAction'
class myModalContainer extends React.Component {
render() {
const { Title } = Typography;
const { onClick } = this.props;
myModalContainer.propTypes = {
onClick: PropTypes.func.isRequired
}
return (
<div className="search-container-parent">
<Row className="search-container">
<Col className="search-col1" xs={24} sm={12}>
<Input size="large" style={{width:'40%'}} id="issueReturnNo" placeholder="QR code here"/>
<Button size="large">SEARCH</Button>
<div className="signBtn-div">
<Button size="large" type="primary" onClick={onClick} >SIGN</Button>
<myModal />
</div>
</Col>
<Col xs={24} sm={12}>
<Title className="issueLog-title" level={3} style={{color:"#F08080"}}>Issue</Title>
</Col>
</Row>
</div>
);
}
}
const mapDispatchToProps = dispatch => ({
onClick: () => dispatch(modalVisibilityOn())
})
export default connect(
null, mapDispatchToProps
)(myModalContainer);
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import rootReducer from './myRootReducer'
const store = createStore(rootReducer,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
serviceWorker.unregister();
I expect the visibility props on myModal.js would be true when the sign button on myModalContainer.js is clicked, but the it keep showing false.
Any help would be appreciated. Thanks!
After lots of researches and trials, It turns out that my code has no problem..
The reason why it is not working as expected, is due to the redux and react-redux version. After switching package.json dependencies versions back to the one that redux official tutorial are using, the application is running without any problem.
In case anyone have the same problem, here is the version I am using now for my app in npm:
redux: ^3.5.2
react-redux: ^5.0.7
Update:
Just found out that the cause of the problem comes from conflicts between older version modules and newer version modules.
Therefore by updating all the dependencies in package.json to the latest version, the app can also run smoothly. It is not necessary to downgrade react-redux and redux.

Redux store updating but mapStateToProps not updating component props

When I click on a pointer on my Google Maps component, I can see my store being updated in Redux Devtools but mapStateToProps does not seem to update my component props. Therefore, my Google Maps pointers <InfoWindow> does not open.
If I navigate to another Link(using react-router) from my NavBar and then navigate back to this page, the component receives the updated props from mapStateToProps and the previously clicked Google Maps pointer has the <InfoWindow> open.
I have been trying to debug this for the past 1 week, tried converting components/ClassSchedule/Map/Pure.jsx to a class component but it did not work.
components/ClassSchedule/Map/index.js
import { connect } from 'react-redux';
import { selectClass } from '../../../actions/index';
import Pure from './Pure';
const mapStateToProps = state => ({
selectedClass: state.classMapTable.selectedClass,
});
const mapDispatchToProps = dispatch => ({
selectClass: id => dispatch(selectClass(id)),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Pure);
components/ClassSchedule/Map/Pure.jsx
import React from 'react';
import MapContent from './MapContent';
const Map = props => {
return (
<MapContent
googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=googleMapsKeyHere`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `550px` }} />}
mapElement={<div style={{ height: `100%` }} />}
{...props}
/>
);
};
export default Map;
components/ClassSchedule/Map/MapContent.jsx
import React from 'react';
import {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker,
InfoWindow,
} from 'react-google-maps';
import { defaultPosition } from '../../../data/mapData';
import { classes } from '../../../data/classData';
const { zoom, centre } = defaultPosition;
const MapContent = withScriptjs(
withGoogleMap(({ selectedClass, selectClass }) => (
<GoogleMap defaultZoom={zoom} defaultCenter={centre}>
{classes.map(c => (
<Marker
key={`map${c.id}`}
icon={
'https://mt.google.com/vt/icon?psize=30&font=fonts/arialuni_t.ttf&color=ff304C13&name=icons/spotlight/spotlight-waypoint-a.png&ax=43&ay=48&text=%E2%80%A2'
}
position={c.coordinates}
onClick={() => selectClass(c.id)}
>
{selectedClass === c.id && (
<InfoWindow>
<React.Fragment>
<div>{c.area}</div>
<div>{`${c.level} ${c.subject}`}</div>
<div>{`${c.day}, ${c.time}`}</div>
</React.Fragment>
</InfoWindow>
)}
</Marker>
))}
</GoogleMap>
))
);
export default MapContent;
reducers/index.js
import { combineReducers } from 'redux';
import { connectRouter } from 'connected-react-router';
import classMapTable from './classMapTable';
export default history =>
combineReducers({
router: connectRouter(history),
classMapTable,
});
reducers/classMapTable.js
const classMapTable = (state = {}, action) => {
switch (action.type) {
case 'SELECT_CLASS':
return { ...state, selectedClass: action.classId };
default:
return state;
}
};
export default classMapTable;
store/index.js
import { createBrowserHistory } from 'history';
import { createStore, applyMiddleware } from 'redux';
import { routerMiddleware } from 'connected-react-router';
import { composeWithDevTools } from 'redux-devtools-extension';
import createRootReducer from '../reducers';
export const history = createBrowserHistory();
export default function configureStore(preloadedState) {
const store = createStore(
createRootReducer(history),
preloadedState,
composeWithDevTools(applyMiddleware(routerMiddleware(history)))
);
return store;
}
actions/index.js
export const selectClass = classId => ({
type: 'SELECT_CLASS',
classId,
});
After debugging for about 2 weeks, I randomly decided to run npm update. Turns out there wasn't any issue with my code, my npm packages were just outdated/not compatible. I have no idea how I had different versions of react and react-dom. EVERYTHING WORKS NOW.
This was in my package.json:
"react": "^16.7.0",
"react-dev-utils": "^7.0.0",
"react-dom": "^16.4.2",
After updating my package.json:
"react": "^16.8.1",
"react-dev-utils": "^7.0.1",
"react-dom": "^16.8.1",
Moral of the story: KEEP YOUR PACKAGES UP TO DATE.

Redux store error: <Provider> does not support changing `store` on the fly

I am trying to setup my first react/redux/rails app. I am using react_on_rails gem to pass in my current_user and gyms props.
Everything appears to work ok so far except my console shows error:
<Provider> does not support changing `store` on the fly. It is most likely that you see this error because you updated to Redux 2.x and React Redux 2.x which no longer hot reload reducers automatically. See https://github.com/reactjs/react-redux/releases/tag/v2.0.0 for the migration instructions.
Googling gives me hints that this can happen if you try to create a store within a render method, which causes store to get recreated. I don't see that issue here. Where am I going wrong?
//App.js
import React from 'react';
import { Provider } from 'react-redux';
import configureStore from '../store/gymStore';
import Gym from '../components/Gym';
const App = props => (
<Provider store={configureStore(props)}>
<Gym />
</Provider>
);
export default App;
../store/gymStore.jsx
//the store creation.
/*
// my original way
import { createStore } from 'redux';
import gymReducer from '../reducers/';
const configureStore = railsProps => createStore(gymReducer, railsProps);
export default configureStore;
*/
/* possible fix: https://github.com/reactjs/react-redux/releases/tag/v2.0.0 */
/* but adding below does not resolve error */
import { createStore } from 'redux';
import rootReducer from '../reducers/index';
export default function configureStore(railsProps) {
const store = createStore(rootReducer, railsProps);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept(() => {
const nextRootReducer = require('../reducers').default;
store.replaceReducer(nextRootReducer);
});
}
return store;
}
I am not sure my rendered component is necessary but in case it is:
//compenents/Gym.jsx
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import LeftMenu from './LeftMenu';
class Gym extends React.Component {
static propTypes = {
//name: PropTypes.string.isRequired // this is passed from the Rails view
};
/**
* #param props - Comes from your rails view.
*/
constructor(props) {
super(props);
this.state = {
current_user: this.props.current_user,
gyms: JSON.parse(this.props.gyms),
active_gym: 1, //JSON.parse(this.props.gyms)[0],
name: 'sean',
title: 'Gym Overview'
};
}
updateName = name => {
this.setState({ name });
};
isLoggedIn = () => {
if (this.state.current_user.id != '0') {
return <span className="text-success"> Logged In!</span>;
} else {
return <span className="text-danger"> Must Log In</span>;
}
};
isActive = id => {
if (this.state.active_gym == id) {
return 'text-success';
}
};
render() {
return (
<div className="content">
<h2 className="content-header">{this.state.title}</h2>
{LeftMenu()}
{this.state.current_user.id != '0' ? <span>Welcome </span> : ''}
{this.state.current_user.first_name}
<h3 className="content-header">Your Gyms</h3>
<ul>
{this.state.gyms.map((gym, key) => (
<li key={key} className={this.isActive(gym.id)}>
{gym.name}
</li>
))}
</ul>
{this.isLoggedIn()}
<hr />
{/*
<form>
<label htmlFor="name">Say hello to:</label>
<input
id="name"
type="text"
value={this.state.name}
onChange={e => this.updateName(e.target.value)}
/>
</form>
*/}
</div>
);
}
}
function mapStateToProps(state) {
return {
current_user: state.current_user,
gyms: state.gyms,
active_gym: state.active_gym
};
}
export default connect(mapStateToProps)(Gym);

Resources