Invalid hook call in two-line functional component - react-hooks

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 />} />

Related

Redux Persist using Redux Toolkit in React

I wanna store states after refreshing the page, so I used Redux Persist and followed the instructions at LogRocket, I failed again and again because my configureStore is different. Can you please tell me how to do it?
Here is my store.js
// store.js
import { configureStore } from "#reduxjs/toolkit";
import appSlice from './appSlice';
const store = configureStore({
reducer: {
app: appSlice.reducer,
}
});
export default store
Here is my index.js
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { App } from './App';
import store from './data/store';
import './index.css'
import reportWebVitals from './reportWebVitals';
import {BrowserRouter as Router, Routes, Route } from 'react-router-dom';
// const store = configureStore({ reducer: App });
// const store = createStore(Reducer)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<Router>
<Routes>
<Route path="/*" element={<App/>} />
</Routes>
</Router>
</Provider>
</React.StrictMode>
);
reportWebVitals();
Thank you in advance!
When I follow the instructions, console shows couldn't find app store
// create store.js like this
import { combineReducers } from 'redux';
import { configureStore } from '#reduxjs/toolkit';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage'; // defaults to localStorage for web
// add the reducers that need to be persisted
const reducersToPersist = [];
const persistConfig = {
key: 'root',
storage,
whitelist: reducersToPersist
};
const reducers = combineReducers({
// Your reducers
});
const persistedReducer = persistReducer(persistConfig, reducers);
const store = configureStore({
reducer: persistedReducer,
});
const persistor = persistStore(store);
export { store, persistor };
in your root file
import { PersistGate } from 'redux-persist/integration/react';
import { Provider } from 'react-redux';
import { persistor, store } from './store';
const App = () => {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
{/* Your Project routes */}
</PersistGate>
</Provider>
);
};
export default App;

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

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',
]
}

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.

TypeError: translate is not a function

There is no errors and warnings in compiling but when i launch my project i get this error :
TypeError: translate is not a function
289 | inputProps={{
> 291 | placeholder: translate('labels.search') + '...',
I'm using react-admin 2.3.3, i can post my packages.json if you want.
I tried to clean my modules and install it again without success.
This is my compoment (simplified code) :
import React from 'react'
import PropTypes from 'prop-types'
import deburr from 'lodash/deburr'
import Autosuggest from 'react-autosuggest'
import match from 'autosuggest-highlight/match'
import parse from 'autosuggest-highlight/parse'
import TextField from '#material-ui/core/TextField'
import Paper from '#material-ui/core/Paper'
import MenuItem from '#material-ui/core/MenuItem'
import Popper from '#material-ui/core/Popper'
import ListItem from '#material-ui/core/ListItem'
import ListItemIcon from '#material-ui/core/ListItemIcon'
import ListItemText from '#material-ui/core/ListItemText'
import Avatar from '#material-ui/core/Avatar'
import Divider from '#material-ui/core/Divider'
import SearchIcon from '#material-ui/icons/Search'
import { withStyles } from '#material-ui/core/styles'
import { fade } from '#material-ui/core/styles/colorManipulator'
import { Link } from 'react-router-dom'
const styles = theme => ({})
class TestComponent extends React.Component {
render() {
const { classes } = this.props
const { translate } = this.context
return (
<div className={classes.root}>
<Autosuggest
inputProps={{
placeholder: translate('labels.search') + '...',
}}
/>
</div>
)
}
}
TestComponent.propTypes = {
classes: PropTypes.object.isRequired,
}
TestComponent.contextTypes = {
translate: PropTypes.func,
}
export default withStyles(styles)(TestComponent)
You must import the translate HOC from react-admin and apply it to your component:
import { translate } from 'react-admin`;
export default translate(withStyles(styles)(TestComponent));
Then, you should get the translate function injected by this HOC from your props instead of context:
const { classes, translate } = this.props

Can't figure out "Error: Actions must be plain objects. Use custom middleware for async actions."

I'm using React and Redux to build an app to request and subsequently display movie information from an API. In the console, I can get the requested data back but somewhere beyond that I hit the error - "Error: Actions must be plain objects. Use custom middleware for async actions."
Here's my code so far..
Search component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchMovie } from '../../actions/index';
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = { term: '' };
this.onInputChange = this.onInputChange.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onInputChange(e) {
this.setState({ term: e.target.value });
}
onFormSubmit(event) {
event.preventDefault();
this.props.fetchMovie(this.state.term);
this.setState({ term: '' });
}
render() {
return (
<div>
<form onSubmit={this.onFormSubmit} className="input-group">
<input
placeholder="Search by movie title, actor, or genre"
className="form-control"
value={this.state.term}
onChange={this.onInputChange}
/>
<span className="input-group-btn">
<button type="submit" className="btn btn-secondary">
Submit
</button>
</span>
</form>
<br />
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchMovie }, dispatch);
}
export default connect(null, mapDispatchToProps)(SearchBar);
Action...
import axios from 'axios';
const API_KEY = '449a384f';
export const FETCH_MOVIE = 'FETCH_MOVIE';
let movies = [];
export function fetchMovie(term) {
const request = axios
.get(`http://www.omdbapi.com/?s=${term}&apikey=${API_KEY}`)
.then(response => {
movies = response.data;
response.json = movies;
})
.catch(error => console.log(error));
return {
type: FETCH_MOVIE,
payload: request,
};
}
Reducer...
import { FETCH_MOVIE } from '../actions/index';
export default function(state = null, action) {
switch (action.type) {
case FETCH_MOVIE:
return [action.payload.data, ...state];
}
return state;
}
CombineReducer...
import { combineReducers } from 'redux';
import MovieReducer from './movie_reducer';
const rootReducer = combineReducers({
movie: MovieReducer,
});
export default rootReducer;
Store...
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
// import ReduxThunk from 'redux-thunk';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import { createStore, applyMiddleware } from 'redux';
import ReduxPromise from 'redux-promise';
import './index.css';
import App from './App';
import Login from './components/login/login';
import reducers from './reducers/reducer';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import { findDOMNode } from 'react-dom';
import $ from 'jquery';
const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<Switch>
<Route path="/Login" component={Login} />
<Route path="/" component={App} />
</Switch>
</BrowserRouter>
</Provider>,
document.querySelector('#root')
);
Thanks for the help!
Vanilla Redux requires that you return a plain JavaScript object in your action creators. Whenever, you need to perform async operations, you need to introduce middleware like redux-thunk or redux-promise to intercept the returned object an perform additional work so that a plain JavaScript object can ultimately be returned.
You're attempting to use redux-promise, but what you're returning is not causing the middleware to be invoked. Your fetchMovie() method is returning a plain object containing a Promise. In order to use redux-promise, the method needs to return a Promise.
From their documentation:
createAction('FETCH_THING', async id => {
const result = await somePromise;
return result.someValue;
});
Probably, the reason is that you're trying to return promise in the action to reducer.
You're using thunk, so you always can dispatch from action creator
export const fetchMovie = term => dispatch => axios
.get(`http://www.omdbapi.com/?s=${term}&apikey=${API_KEY}`)
.then(response => {
dispatch({
type: FETCH_MOVIE,
payload: response,
});
})
.catch(error => console.log(error));

Resources