Im trying to build a simple app with react-boilerplate, the containers(basically views) and components do not use classes to create new components, instead they are basic functions. Typically you add a constructor or direct state to a class, since there are no classes i dont know where to add the state definition. Nowhere ive added a constructer or state definition works, where can you add simple react (NO redux) state list or constructor within this pattern for this view?
example of a container
/*
* HomePage
*
* This is the first thing users see of our App, at the '/' route
*/
import React, { useEffect, memo } from 'react';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { createStructuredSelector } from 'reselect';
import { useInjectReducer } from 'utils/injectReducer';
import { useInjectSaga } from 'utils/injectSaga';
import {
makeSelectRepos,
makeSelectLoading,
makeSelectError,
} from 'containers/App/selectors';
import H2 from 'components/H2';
import ReposList from 'components/ReposList';
import AtPrefix from './AtPrefix';
import CenteredSection from './CenteredSection';
import Form from './Form';
import Input from './Input';
import Section from './Section';
import messages from './messages';
import { loadRepos } from '../App/actions';
import { changeUsername, setHomeVisible } from './actions';
import { makeSelectUsername, makeHomeVisible } from './selectors';
import reducer from './reducer';
import saga from './saga';
import NavBar from './NavBar';
import ButtonLink from './ButtonLink';
const key = 'home';
export function HomePage({
username,
loading,
error,
repos,
onSubmitForm,
onChangeUsername,
homeVisible,
makeHomeVisible,
setHomeVisible
}) {
useInjectReducer({ key, reducer });
useInjectSaga({ key, saga });
useEffect(() => {
// When initial state username is not null, submit the form to load repos
if (username && username.trim().length > 0) onSubmitForm();
}, []);
const reposListProps = {
loading,
error,
repos,
};
return (
<article>
<Helmet>
<title>title</title>
<meta
name="description"
content=""
/>
</Helmet>
<NavBar>
<ButtonLink to="/" onClick={makeHomeVisible}>
<FormattedMessage {...messages.websites} />
</ButtonLink>
<ButtonLink to="/apps" >
<FormattedMessage {...messages.apps} />
</ButtonLink>
</NavBar>
<div className={`home ${this.state.visible === false ? 'hidden': ''}`}>
<Section>
<H2>
<FormattedMessage {...messages.trymeHeader} />
</H2>
<Form onSubmit={onSubmitForm}>
<label htmlFor="username">
<FormattedMessage {...messages.trymeMessage} />
<AtPrefix>
<FormattedMessage {...messages.trymeAtPrefix} />
</AtPrefix>
<Input
id="username"
type="text"
placeholder="mxstbr"
value={username}
onChange={onChangeUsername}
/>
</label>
</Form>
<ReposList {...reposListProps} />
</Section>
</div>
</article>
);
}
HomePage.propTypes = {
loading: PropTypes.bool,
homeVisible: PropTypes.bool,
error: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
repos: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
onSubmitForm: PropTypes.func,
username: PropTypes.string,
onChangeUsername: PropTypes.func,
makeHomeVisible: PropTypes.func,
};
const mapStateToProps = createStructuredSelector({
repos: makeSelectRepos(),
username: makeSelectUsername(),
loading: makeSelectLoading(),
error: makeSelectError(),
homeVisible: makeHomeVisible(),
});
export function mapDispatchToProps(dispatch) {
return {
onChangeUsername: evt => dispatch(changeUsername(evt.target.value)),
makeHomeVisible: evt => dispatch(setHomeVisible(evt.target.value)),
onSubmitForm: evt => {
if (evt !== undefined && evt.preventDefault) evt.preventDefault();
dispatch(loadRepos());
},
};
}
const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
export default compose(
withConnect,
memo,
)(HomePage);
As per the React Docs, you can use the useState hook to save data to state. There is no "state list or constructor" but you can simply access the props you pass in and decide to save those into state if they are going to be manipulated - if not stick to using the props.
Codesandbox Demo
Basic Example:
function Child(props) {
let [name, setName] = useState(props.data.name);
function updateName () {
setName('The Dude');
}
return (
<div className="App">
<h2>{name}</h2>
<button onClick={updateName}>Update</button>
</div>
);
};
function App() {
let [data, setData] = useState({name: 'dude'});
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Child data={data}/>
</div>
);
}
Related
I'm running into an issue where my action is not being recognized as a function in my useEffect(). I already checked to see if I was importing or dispatching incorrectly, but perhaps I'm missing something.
In my case, I want all of my feedback from the payload, so I don't need to pass anything specific through the action. I just want it to fetch the action when the feedback is empty.
import React, { useState, useEffect } from 'react';
import { withStyles } from '#material-ui/core/styles';
import PropTypes from 'prop-types';
import { connect, useSelector } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { requestFeedback } from 'common/state/feedback/actions';
import MUIDataTable from 'mui-datatables';
import { feedbackColumns } from 'components/DataTable/feedbackColumnHelper';
import Message from 'components/Message';
import ErrorMessage from 'components/ErrorMessage';
import { getIsUserAdmin } from 'common/state/auth/selectors';
import {
getFeedbackList,
getFeedbackErrorKey,
} from 'common/state/feedback/selectors';
import ParameterCrumbs from 'components/DataTable/Header/ParameterCrumbs';
import HelmetIoT from 'components/HelmetIoT';
import BreadCrumbs from 'components/Breadcrumbs';
import styles from './styles';
export const FeedbackPage = ({
userIsAdmin,
// timestamp,
feedbackErrorKey,
feedbackRows,
loadAllFeedback,
}) => {
const hasNoFeedbackLength = !feedbackRows?.length;
const feedbackViewData = useSelector((state) => getFeedbackList(state));
useEffect(() => {
if (hasNoFeedbackLength && !feedbackErrorKey) {
loadAllFeedback();
}
}, [hasNoFeedbackLength, feedbackErrorKey, loadAllFeedback]);
const tableOptions = {
draggableColumns: { enabled: true },
pagination: true,
rowsPerPage: 10,
print: false,
};
return (
<div>
<HelmetIoT preTitle={`Feedback Page `} />
<BreadCrumbs crumbs={[{ title: `Feedback Page` }]} />
<ParameterCrumbs />
{userIsAdmin && feedbackErrorKey && (
<div className="h-padding-t-default">
<ErrorMessage>There was an error loading feedback</ErrorMessage>
</div>
)}
{!feedbackErrorKey && userIsAdmin && (
<div className="h-padding-t-default">
<ErrorMessage>
You do not have permission to access this page
</ErrorMessage>
</div>
)}
{feedbackErrorKey && !userIsAdmin && (
<div className="h-padding-t-default">
<ErrorMessage>
There was an error loading feedback and you do not have permission
to access this page
</ErrorMessage>
</div>
)}
{!feedbackRows?.length && (
<div className="h-padding-t-default">
<Message>No feedback has been entered</Message>
</div>
)}
{!feedbackErrorKey && feedbackRows?.length && (
<MUIDataTable
title={'Feedback'}
data={feedbackViewData}
columns={feedbackColumns}
options={tableOptions}
/>
)}
</div>
);
};
FeedbackPage.propTypes = {
feedbackErrorKey: PropTypes.string,
feedbackRows: PropTypes.array,
loadAllFeedback: PropTypes.func,
};
const mapDispatchToProps = {
loadAllFeedback: requestFeedback,
};
const mapStateToProps = createStructuredSelector({
feedbackErrorKey: getFeedbackErrorKey,
feedbackRows: getFeedbackList,
userIsAdmin: getIsUserAdmin,
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(FeedbackPage));
×
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
import './App.css';
import {useSelector} from 'react-redux';
import Header from './Header';
import Sidebar from './Sidebar';
import Feed from './Feed';
import { selectUser } from './features/userSlice';
import Login from './Login';
function App() {
const user = useSelector(selectUser)
return (
<div className="app">
<Header />
{!user ? (
<Login />
) : (
<div className="app__body">
<Sidebar />
<Feed />
{/* Widgets */}
</div>
)}
</div>
);
}
export default App;
import { createSlice } from '#reduxjs/toolkit';
export const userSlice = createSlice({
name: 'user',
initialState: {
user: null,
},
reducers: {
login: (state, action) => {
state.value = action.payload;
},
logout: (state) => {
state.user = null;
},
},
});
export const { increment, decrement, incrementByAmount } = userSlice.actions;
// Selectors
export const selectUser = (state) => state.user.user;
export default userSlice.reducer;
I am working through the React/Redux exercise on FreeCodeCamp.org. After finishing the exercise I want to take it a step farther by implementing it on a local host, and then deploying it to github.
So far, I've used npx create-react-app {appname} in node. Replaced, the original app.js file with my own code that I have below and tried to locally host with npm start on Node.
When that didn't work I did some googling and found the following command to install redux npm install redux react-redux redux-thunk --save. when that didn't work I tried adding the fourth line of my code import { createStore } from 'redux';
Currently I am facing the current error
What can I do to get my app to locally host, so I can then move on to the next step of deploying it to Github?
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { createStore } from 'redux';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message: message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
// Change code below this line
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState({
input: '',
messages: this.state.messages.concat(this.state.input)
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
// Change code above this line
const mapStateToProps = (state) => {
return {messages: state}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message))
}
}
};
const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);
class AppWrapper extends React.Component {
render() {
return (
<Provider store={store}>
<Container/>
</Provider>
);
}
};
ReactDOM.render(<AppWrapper/>, document.getElementById("root"))
you didn't import them;
import ReactDOM from 'react-dom';
import Redux from 'redux';
import ReactRedux from 'react-redux';
by the way there is a cleaner way to import what you need. you will need a little refactoring for that:
import {render} from 'react-dom';
import {Provider, createStore} from 'redux';
import {connect} from 'react-redux';
you get the idea, instead of importing the default export, you import selectively and in your code just use the named import instead of default.name
(e.g.
ReactDOM.render(<AppWrapper/>, document.getElementById("root")) becomes
render(<AppWrapper/>, document.getElementById("root"))
Check out this SO answer for further reading
When i enter my React Router-dom i point my routes to a validation page.
In event aim loged in or not, i push my route(history) to required page but i keep getting bellow error.
Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, compare, WrappedComponent, displayName}). If you meant to render a collection of children, use an array instead.
in Unknown (at RequiredAuth.js:34)
in RequireAuth (created by ConnectFunction)
in ConnectFunction (created by Context.Consumer)
in Route (at App.js:23)
in Switch (at App.js:18)
in Router (at App.js:17)
in div (at App.js:16)
in App (created by ConnectFunction)
in ConnectFunction (at Dashbord.js:14)
in div (at Dashbord.js:14)
in DashBoard (created by ConnectFunction)
in ConnectFunction (at src/index.js:10)
in Provider (at src/index.js:9)
App.js
import React from 'react';
import { Router, Route, Redirect, Switch } from 'react-router-dom';
import { history } from './configureStore';
import { allRoutes } from './routes';
import NotFound from './pages/404';
import RequiredAuth from './components/RequiredAuth';
import NotRequiredAuth from './components/NotRequiredAuth';
import DashBoard from './pages/Dashbord';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
class App extends React.Component {
render() {
return (
<div style={{ height: '100%' }}>
<Router history={history}>
<Switch>
{allRoutes
.filter(route => route.visible)
.map((route, index) => {
return (
<Route
exact={route.exact}
path={route.path}
key={index}
component={RequiredAuth(route.component)}
/>
)
})}
<Route path={'/:404_path'} key={'404'} component={NotFound} />
<Redirect to="/dashboard" />
</Switch>
</Router>
</div>
);
}
}
App.displayName = 'App';
const mapDispatchToProps = dispatch => {
return bindActionCreators({ }, dispatch);
};
const mapStateToProps = state => {
return {
};
};
export default DashBoard(
connect(
mapStateToProps,
mapDispatchToProps
)(App)
);
RequiredAuth.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import PropTypes from 'prop-types';
import { history } from '../configureStore';
export default function (ComposedComponent) {
class Authentication extends Component {
constructor(props){
super(props);
this.props = props;
}
componentDidMount() {
const { auth } = this.props
if (!auth.success) {
history.push('/login');
}
}
componentDidUpdate() {
const { auth } = this.props
if (!auth.success) {
history.push('/login');
}
}
PropTypes = {
router: PropTypes.object
}
render() {
return <ComposedComponent {...this.props} />;
}
}
Authentication.propTypes = {
location: PropTypes.object
}
Authentication.displayName = 'RequireAuth'
function mapStateToProps(state) {
return { auth: state.auth };
}
const mapDispatchToProps = dispatch => bindActionCreators({ }, dispatch);
return connect(mapStateToProps, mapDispatchToProps)(Authentication);
}
Dashbord.js
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { ShouldRender } from '../components/basic/ShouldRender';
export default function(ComposedComponent) {
class DashBoard extends Component {
render() {
const {auth} = this.props
if (!auth.success) return <div>{ComposedComponent && <ComposedComponent />}</div>;
return (
<div>
<ShouldRender if={!auth.success}>
{ComposedComponent && <ComposedComponent />}
</ShouldRender>
<ShouldRender if={auth.success}>
<div style={{ height: '100%' }}>
<div>
<div className='page-container'>
<main className='main-content bgc-grey-100'>
<div id='mainContent'>
<div className='row gap-20 masonry pos-r'>
<div className='masonry-item col-12'>
{ComposedComponent && <ComposedComponent />}
</div>
</div>
</div>
</main>
<footer className='bdT ta-c p-30 lh-0 fsz-sm c-grey-600'>
<span>
Copyright © {new Date().getFullYear()}{' '}
<a
href='https://dataintegrated.co.ke'
target='_blank'
title='Data Integrated'
>
Data Integrated Limited
</a>
. All rights reserved.
</span>
</footer>
</div>
</div>
</div>
</ShouldRender>
</div>
);
}
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ }, dispatch);
};
function mapStateToProps(state) {
return {
auth: state.auth
};
}
DashBoard.propTypes = {
};
return connect(mapStateToProps, mapDispatchToProps)(DashBoard);
}
Have tried working with HOC react official page but when i implement this validation, i always land my self on this error, but when i remove HOC implementation everything renders perfectly.
try using a return statement in your HOC or change your function into an arrow function
I.E
export default function(ComposedComponent) {
return class DashBoard extends Component
OR
export default (ComposedComponent) =>{
class DashBoard extends Component
I found several errors in your code, first of all your function definition is wrong, at export default function(ComposedComponent) it must be export default function ComposedComponent () {} Do not put this class class Authentication extends Component {} inside the function, if you want it to be a component create another function for this. Also define which type of components you are going to use: Functional or Class based.
We cannot know how to help you while your code have these several mistakes. Please check them and back again with your updated code.
I am unable to make the store available to children components.
The setup is a SPA with Symfony as back-end, though this should not make a difference for this matter.
The entry point for Webpack is the file:
/client/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux';
import ReduxPromise from 'redux-promise';
import Root from './App';
import registerServiceWorker from './registerServiceWorker';
import reducers from './pages/combine_reducers';
let composeEnhancers = typeof(window) !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(
reducers,
composeEnhancers(
applyMiddleware(ReduxPromise)
)
)
ReactDOM.render(
<Root store={store} />
, document.querySelector('#root')
);
registerServiceWorker();
The apps as such is at:
/client/App.js
import React from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import {
BrowserRouter as Router,
Route,
Link,
Switch
} from 'react-router-dom';
import HomePage from './pages/home/';
import AccountPage from './pages/account/';
const Root = ({ store }) => {
return(
<Provider store={store}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<Router>
<div>
<Link to="/account">Account</Link>
<Link to="/">Home</Link>
<div>
<Switch>
<Route path="/account" component={AccountPage} />
<Route path="/" component={HomePage} />
</Switch>
</div>
</div>
</Router>
</div>
</Provider>
)
}
Root.propTypes = {
store: PropTypes.object.isRequired
}
export default Root;
So far so good. The store is available in App.js.
But that's not the case at the next level. As you can see I'm attempting to make the store available using connect().
/client/pages/home/index.js
import React from 'react';
import { connect } from 'react-redux';
import Register from '../common/register/';
import PropTypes from 'prop-types';
class Home extends React.Component {
constructor(props){
super(props)
console.log(props);
}
render() {
return (
<div>
<h1> Hello World from home! </h1>
<Register />
</div>
);
}
}
Home.propTypes = {
store: PropTypes.object.isRequired
}
const mapStateToProps = (state) => {
return {
store: state.store,
}
}
export default connect(mapStateToProps)(Home)
At the lower level, the Register component, I'm able to submit the form, but the store not being available, I am unable to capture the response coming from the server.
/client/pages/common/register/index.js
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import RegisterForm from './containers/register';
import { actionSubmitRegister } from './actions/';
import PropTypes from 'prop-types';
class Register extends React.Component{
constructor (props) {
super(props);
this.state = {
registerResponse: '',
}
this.onSubmitRegister = this.onSubmitRegister.bind(this);
}
onSubmitRegister (event) {
event.preventDefault();
let submitForm = new Promise((resolve, reject) => {
actionSubmitRegister(this.props.form.RegisterForm.values);
});
submitForm.then((response) => {
console.log('response',response);
this.setState({registerResponse: this.props.submit_register.data});
console.log('registerResponse', this.state.registerResponse);
}).catch((error) => {
console.log(error);
});
}
render(){
return (
<div>
<div>
<RegisterForm
submitRegister={this.onSubmitRegister}
/>
<h3>{this.state.registerResponse}</h3>
</div>
</div>
)
}
}
/*
Register.propTypes = {
store: PropTypes.object.isRequired
}
*/
const mapStateToProps = (state) => {
return {
form: state.form,
submit_register: state.submit_register,
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({actionSubmitRegister}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);
In mapStateToProps you map store: state.store but in general you use this method to map single props from your state to props in your component, not map the entire store (if this is even possible).
Eg:
form: state.form
The reason you are not able to access the store object in props is because you are not passing it down via props.
Provider from the react-redux library, makes it available to all children down the element tree. Store is made available via React's context API, NOT via props.
"Context is designed to share data that can be considered “global” for a tree of React components."
So in a child component of Provider, we can now do something like
render() {
const { store } = this.context;
console.log(store)
return(
...
)
}
This is the same way that react-redux's connect HOC is able to access the store and subsequently mapStateToProps or utilise the store's dispatch method to mapDispatchToProps.
Also I think Provider requires that it’s child element is a React component.
Check out this tutorial for a more in-depth explanation.
After the input I received above, I reviewed my code and got it to work.
Actually the main issue was on the /client/pages/common/register/index.js file, but I am posting the whole chain for reference:
/client/index.js
nothing to change
/client/App.js
The references to propTypes do not seem to be necessary, so I took them out.
import React from 'react';
import { Provider } from 'react-redux';
import {
BrowserRouter as Router,
Route,
Link,
Switch
} from 'react-router-dom';
import HomePage from './pages/home/';
import AccountPage from './pages/account/';
const Root = ({ store }) => {
return(
<Provider store={store}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<Router>
<div>
<Link to="/account">Account</Link>
<Link to="/">Home</Link>
<div>
<Switch>
<Route path="/account" component={AccountPage} />
<Route path="/" component={HomePage} />
</Switch>
</div>
</div>
</Router>
</div>
</Provider>
)
}
export default Root;
/client/pages/home/index.js
Here both propTypes and connect() do not seem to be required.
import React from 'react';
import Register from '../common/register/';
class Home extends React.Component {
constructor(props){
super(props)
}
render() {
return (
<div>
<h1> Hello World from home! </h1>
<Register />
</div>
);
}
}
export default Home;
/client/pages/common/register/index.js
The main issue here was the onSubmitRegister() method. The promise was not properly setup and I was referencing the action directly instead of using this.props. React do not seem to like that.
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import RegisterForm from './containers/register';
import { actionSubmitRegister } from './actions/';
class Register extends React.Component{
constructor (props) {
super(props);
this.state = {
registerResponse: '',
}
this.onSubmitRegister = this.onSubmitRegister.bind(this);
}
onSubmitRegister (event) {
event.preventDefault();
let submitForm = new Promise((resolve) => {
resolve(this.props.actionSubmitRegister(this.props.form.RegisterForm.values));
});
submitForm.then((result) => {
let data = result.payload.data;
this.setState({registerResponse: data.message});
}).catch((error) => {
console.log(error);
});
}
render(){
return (
<div>
<div>
<RegisterForm
submitRegister={this.onSubmitRegister}
/>
<h3>{this.state.registerResponse}</h3>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
form: state.form,
submit_register: state.submit_register,
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({actionSubmitRegister}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);