I am trying to understand how to get the props to show up when exporting a functional component like this
export const functionName = ({...props}) => {
console.log(props); // undefined
return <p>Hello</p>
}
functionaName.propTypes = { // propTypes here }
const mapStateToProps = state => ({ // state stuff here })
const mapDispatchToProps = { // props mapping here }
export default connect(mapStateToProps, mapDispatchToProps)(functionName); // works like it should
The normal method works as it should
functionaName.propTypes = { // propTypes here }
const mapStateToProps = state => ({ // state stuff here })
const mapDispatchToProps = { // props mapping here }
export default connect(mapStateToProps, mapDispatchToProps)(functionName);
I am trying to organize my imports and have a folder structure like so
components
item // contains the component
index.jsx
index.js
index.js
export * from './item';
Then doing an import would be like this
import { functionName } from '../components';
My actual code does have a return value, I just truncated it for this post. Any ideas on how to accomplish what I am looking to achieve?
For this code to work:
import { functionName } from '../components';
You need your components/index.js too look like this:
import { someFunction } from './item';
export someFunction;
// Or one liner:
export { someFunction } from './item';
Here are some examples from one of my projects:
export { default as AutoSuggest } from './AutoSuggest/AutoSuggest.react';
export { ErrorLog } from './ErrorLog/ErrorLog.react';
export { default as IconsBar } from './IconsBar/IconsBar.react';
export { JobDetails, JobEntry, JobGraph } from './Job';
export { default as LogsViewer } from './LogsViewer/LogsViewer.react';
export { default as Menu } from './Menu/Menu.react';
export { default as MultiSelect } from './MultiSelect/MultiSelect.react';
export { default as Notifications } from './Notifications/Notifications.react';
export { default as Panel } from './Panel/Panel.react';
export { ColorProperty } from './Theme';
Related
The code is long, but the idea is this:
I have 3 Components
Base App component, MapComponent , ShipComponent
App components calls out MapComponent using container, MapComponent calls out ShipComponent lso using the container.
Both Map and Ship containers are connected to store.
Map and Ship component use same multiarray data and is passed to mapStateToProps
In component Ship with the use of useEffect() an action is being called out to generate the data and put it inside the store.
The issue:
The child component (component Ship) re-renders just fine BUT the component Map (parent) does NOT even tho the parent Map component ALSO uses same shipLocation array. Both map and Ship component gets it from same redux store.
Why does child component after executing an action re-renders yet parent component doesn't? How do I fix it?
As a test, Instead of passing the action thru mapDispatchToProps to ShipComponent, I gave it to MapComponent and thru props passed it to ShipComponent for it to execute. Only then it updated the parent MapComponent. But I need ShipComponent to get the action and update MapComponent.
EDIT Added code example:
So root component (App)
import React from "react";
import { Provider } from "react-redux";
import Map from "../containers/Map";
import mainStore from "../store";
const store = mainStore();
function AppComponent(props) {
return (
<Provider store={store}>
<Map />
</Provider>
);
}
export default AppComponent;
that calls out container Map that passes all the state data from store to component MapComponent
import { connect } from "react-redux";
import MapComponent from "../components/MapComponent";
const mapStateToProps = ({ shipR }) => {
return {
shipLocation: [...shipR.shipLocation],
};
};
const mapDispatchToProps = (dispatch) => {
return {
};
};
const Map = connect(mapStateToProps, mapDispatchToProps)(MapComponent);
export default Map;
MapComponent:
import React from "react";
import { Provider } from "react-redux";
import mainStore from "../store";
import Ship from "../containers/Ship";
const store = mainStore();
function MapComponent(props) {
return (
<div id="__Map__">
{
<Provider store={store}>
<Ship />
</Provider>
}
</div>
);
}
export default MapComponent;
and this MapComponent calls out ShipComponent container
import { connect } from "react-redux";
import ShipComponent from "../components/ShipComponent";
import { generateDefaultShips, shipToggle } from "../actions/shipAction";
const mapStateToProps = ({ shipR }) => {
return {
shipLocation: [...shipR.shipLocation],
};
};
const mapDispatchToProps = (dispatch) => {
return {
genShips() {
dispatch(generateDefaultShips());
},
};
};
const Ship = connect(mapStateToProps, mapDispatchToProps)(ShipComponent);
export default Ship;
and the ShipComponent component looks like this:
import React, { useEffect } from "react";
function ShipComponent(props) {
useEffect(() => {
props.genShips();
}, []);
return (
<div className="ship"></div>
);
}
export default ShipComponent;
The genShips() is an action:
import { SHIP_GENERATION_RESULT } from "../constants";
export function generateDefaultShips() {
let arr = [];
for (let x = 0; x < 7; x++) {
arr[x] = [];
for (let y = 0; y < 11; y++) arr[x][y] = null;
}
return { type: SHIP_GENERATION_RESULT, ships: arr };
}
The MapComponent reducer:
const initiateState = {
};
function mapReducer(state = initiateState, action) {
return state;
}
export default mapReducer;
and ShipComponent reducer:
import {
SHIP_GENERATION_RESULT,
} from "../constants";
const initiateState = {
shipLocation: [],
};
function shipReducer(state = initiateState, action) {
switch (action.type) {
case SHIP_GENERATION_RESULT: {
return {
...state,
shipLocation: action.ships,
};
}
default:
return state;
}
}
export default shipReducer;
and constants index.js contains:
export const SHIP_GENERATION_RESULT = "SHIP_GENERATION_RESULT";
All of these containers, constants, components and actions are in different files
p.s. the child component ShipComponent gets the shipLocation multiarray data just fine, but since MapComponent (parent) does not re-render he does not.
O and the reduxers index.js:
import { combineReducers } from "redux";
import mapReducer from "./mapReducer";
import shipReducer from "./shipReducer";
const allReducers = combineReducers({
mapR: mapReducer,
shipR: shipReducer,
});
export default allReducers;
store index.js:
import { createStore, applyMiddleware } from "redux";
import thunkMiddleware from "redux-thunk";
import allReducers from "../reducers";
import { composeWithDevTools } from "redux-devtools-extension";
export default function mainStore(prevState) {
return createStore(
allReducers,
prevState,
composeWithDevTools(applyMiddleware(thunkMiddleware))
);
}
You are using different store for App and Map component, remove the store and its provider in MapComponent should be helped. There should only 1 redux store in all app to make all data is consistent.
Im having this issue where even though I'm exporting both components
SearchBar & MainCard I keep getting this error message in my
App.js file. Any feedback is appreciated!
Error:
./src/App.js - Attempted import error: 'MainCard' is not exported from './components/ui-componets/SearchBar'.
App.js
import React, { Component } from 'react';
import { Container, Theme } from '#customLibrary/core'
import { connect } from 'react-redux';
import { fetchComponent } from './actions';
import TopMenu from './components/ui-componets/TopMenu';
import {SearchBar,MainCard} from './components/ui-componets/SearchBar';
class App extends Component {
state = {
visible: true,
width: 13,
}
handleClick = () => {
this.setState({ visible: !this.state.visible, width: 16 })
}
render() {
const { userData } = this.props;
const { visible } = this.state;
return <Theme>
<Container width='3379px'>
</Container>
<Container width='3379px'>
<TopMenu onClick={this.handleClick} userData={userData} />
</Container>
<Container width='3379px'>
<SearchBar />
</Container>
<Container width='3379px'>
<MainCard/>
</Container>
</Theme>
}
}
const mapStateToProps = (state) => {
return {
userData: state.user
}
}
export default connect(mapStateToProps, { fetchComponent })(App);
SearchBar.js
import React, { Component } from 'react';
import { IS_FETCHING_DBUSERS, FETCH_DBUSERS_SUCCESS, IS_FETCHING_ROLES, FETCH_ROLES_SUCCESS, IS_FETCHING_RESOURCES, FETCH_RESOURCES_SUCCESS } from '../../actions/keys';
import { users, Roles, Resources } from '../../actions/URI';
import { fetchComponent } from '../../actions/index';
import { connect } from 'react-redux';
import _ from 'lodash';
import {
Theme,
Grid, Form, Icon, Container, Loader,
Card, Typography, Tabs
} from '#customLibrary/core';
class SearchBar extends Component {
...
}
class MainCard extends Component {
...
}
const mapStateToProps = state => {
return {
Users: state.users,
roles: state.roles,
resources: state.resources,
}
}
export default connect(mapStateToProps, { fetchComponent })(SearchBar,MainCard);
import {SearchBar,MainCard} from './components/ui-componets/SearchBar';
You have used default export while exporting, but using the syntax for named imports while importing.
import CustomName from './components/ui-componets/SearchBar';
should work
Issue: You've not correctly exported MainCard, and you export them as a default export (i.e. only one component is exported), but you import them as named exports.
export default connect(mapStateToProps, { fetchComponent })(SearchBar,MainCard);
Solution: From what I know, the connect HOC can only decorate a single component at a time. If you want to export both then you'll need to connect both individually as named exports (i.e. remove the default keyword.
export const ConnectedSearchBar = connect(mapStateToProps, { fetchComponent })(SearchBar);
export const ConnectedMainCard = connect(mapStateToProps, { fetchComponent })(MainCard);
Now the import in App will work
import { SearchBar, MainCard } from './components/ui-componets/SearchBar';
I have multiple components (screens) connected like this:
import { setNavigationHeader } from './actions';
const MyScreen = () => {
useEffect(() => {
props.setNavigationHeader('MyScreen title');
});
...
}
const mdtp = {
setNavigationHeader
}
export default connect(null, mdtp)(MyScreen);
Is there any way to "inject" this into the component so I can reuse it for multiple components? Maybe with a HOC?
I'm pretty new to React so I don't know what would be the best course of action here.
Also, I would need to be able add more actions to the mapDispatchToProps or to add a mapStateToProps if I want.
The connect function actually returns a HOC, you just need to create it once and reuse it inside your screen components
// navigation.utils.js
import { setNavigationHeader } from './actions';
export const connectScreen = connect(null, {setNavigationHeader});
// MyScreen.js
import { connectScreen } from './navitation.utils'
const MyScreen = ...
export default connectScreen(MyScreen);
// MyScreen2.js
import { connectScreen } from './navitation.utils'
const MyScreen2 = ...
export default connectScreen(MyScreen2);
I am new to react and react-redux. I am learning the redux example about the Shopping-cart, here is the link enter link description here
And I have two questions:
In containers/ProductsContainer.js, the connect passed the { addToCart } like this:
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { addToCart } from '../actions'
import { getVisibleProducts } from '../reducers/products'
import ProductItem from '../components/ProductItem'
import ProductsList from '../components/ProductsList'
......
export default connect(
mapStateToProps,
{ addToCart }
)(ProductsContainer)
I do not understand how the { addToCart } can be passed into connect
2.The addToCard is like this:
export const addToCart = productId => (dispatch, getState) => {
if (getState().products.byId[productId].inventory > 0) {
dispatch(addToCartUnsafe(productId))
}
}
where does getState parameter come from?
Definition of connect function is
export default connect(
mapStateToProps,
mapDispatchToProps
)(SomeComponent)
Here { addToCart } passed directly in place of mapDispatchToProps. It is like {addToCart : addToCart } which is called shorthand syntax.
I have the following parent component:
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux';
import _ from "lodash";
import ChildComponent from "./ChildComponent";
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div>
I'm at Parent
<ChildComponent/>
</div>
);
}
}
function mapStateToProps(state) {
return { }
}
export default connect(mapStateToProps, null)(ParentComponent);
Inside the parent has a component called ChildComponent that looks like this:
import React, { Component, PropTypes } from "react";
import { connect } from "react-redux";
import { reduxForm } from "redux-form";
import { bindActionCreators } from "redux";
class ChildComponent extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
}
render() {
return (
<div>
at the child
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
},
dispatch
);
}
function mapStateToProps(state) {
return {
};
}
export default connect(mapStateToProps, mapDispatchToProps)(
ChildComponent
);
When I try adding the child component I keep getting this error:
But if I click continue the page turns back to normal. I don't understand how the child component is undefined. It's just embedded and does not include any props.
UPDATE:
I'm not getting the error anymore but I notice my page turns blank when I open this particular component. I'll be doing a bit more troubleshooting.
I tried out your code, and it works fine for me. My thought was maybe what your entry file looks like? or file structure? If you like you can try the following syntax for the parent and child - it worked this way as well:
Child:
const mapStateToProps = () => {
return {}
}
const ConnectedChildComponent = connect(
mapStateToProps,
{})(ChildComponent)
export default ConnectedChildComponent;
Parent:
const mapStateToProps = () => {
return {}
}
const ConnectedParentComponent = connect(
mapStateToProps,
{})(ParentComponent)
export default ConnectedParentComponent;
In your ParentComponent change:
import ChildComponent from "./ChildComponent";
to
import ChildComponent from "./ChildComponent.jsx";
i.e. add the missing ".jsx" extension. Your code is most likely determining the import to be a ".js" file by default, whereas it's actually a ".jsx" file.