How can I get access to props of a root component?
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import cartReducer from './store/cartReducer'
import AddToCart from './cart/addToCart.jsx'
const store = createStore(cartReducer);
render(<Provider store={store}><AddToCart clicked={this.props.onIncrementCounter} />
</Provider>, document.getElementById('addToCart'));
Is it possible? Because I have an error: " Cannot read property 'props' of undefined".
I am new in React.
Here is AddToCart component
import React, { Component } from 'react';
import { connect } from 'react-redux';
class AddToCart extends Component {
constructor(props) {
super(props);
}
}
const mapStateToProps = state => {
return {
items: state.cartItems,
count: state.cartItemCount
};
};
const mapDispatchToProps = dispatch => {
return {
onIncrementCounter: () => dispatch({ type: 'INCREMENT' })
};
}
export default connect(mapStateToProps, mapDispatchToProps)(AddToCart);
you got this error " Cannot read property 'props' of undefined" because in that part of your code : <AddToCart clicked={this.props.onIncrementCounter} />
this is referencing anything
you can try this approach:
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import cartReducer from './store/cartReducer'
import AddToCart from './cart/addToCart.jsx'
const store = createStore(cartReducer);
render(<Provider store={store}><AddToCart/>
</Provider>, document.getElementById('addToCart'));
AddToCart.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
class AddToCart extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div onClick={this.props.onIncrementCounter}>
Increment
</div>
)
}
}
const mapStateToProps = state => {
return {
items: state.cartItems,
count: state.cartItemCount
};
};
const mapDispatchToProps = dispatch => {
return {
onIncrementCounter: () => dispatch({ type: 'INCREMENT' })
};
}
export default connect(mapStateToProps, mapDispatchToProps)(AddToCart);
Related
Hello i am just starting to learn redux and am currently having a problem, i have an api i want to get information from and use it in different components i would appreciate if you help me
import React from 'react';
import { render } from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunk from "redux-thunk";
import { createLogger } from "redux-logger";
import { BrowserRouter} from "react-router-dom";
import Reducer from './Reducers';
import App from './App';
import fetchSimcards from './Actions/fetchSimcards';
const middleware = [ thunk ];
middleware.push( createLogger() );
const store = createStore(
Reducer
applyMiddleware(...middleware),
);
import * as serviceWorker from './serviceWorker';
store.dispatch(fetchSimcards());
render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
and this is my action file
import * as type from '../Constans/ActionTypes';
export const ReceiveSimcards = Simcards => ({
type: type.RECEIVE_SIMCARDS,
Simcards
});
this is my reducer file
import { combineReducers } from "redux";
const Simcards = ( state = {}, action ) => {
console.log( state, action );
return state;
};
export default combineReducers({
Simcards
});
this is my container file for simcards
import React, {Component} from 'react';
import SimcardList from "../Component/SimcardList";
import { connect } from "react-redux";
class SimcardContainer extends Component {
render() {
const Simcards = this.props;
return (
<div>
<SimcardList title={"Simcards"} />
<div className="TableNumberItem">{Simcards.SimCardNumber}</div>
<div className="TableNumberItem">{Simcards.SimCardDescription}</div>
<div className="TableNumberItem">{Simcards.TeammatePrice}</div>
</div>
);
}
}
export default connect()(SimcardContainer);
and i want show this container in home page
With redux, you should call all API and handling logic code in action.
Example with action fetchAPI:
export const fetchAPI = () = async dispatch => {
let response = null;
try {
response = await axios.get('api/...')
// Example use axios
dispatch(fetchSuccess(response.data))
// To handle in reducer with redux
} catch (error) {
... Handle error here
}
}
const fetchSuccess = data => ({
type: FETCH_SUCCESS,
data: response.data
})
And in your component, you can use connect to get state and action:
import { bindActionCreators } from 'redux';
import React, { Component } from 'react';
import SimcardList from "../Component/SimcardList";
import { connect } from "react-redux";
import * as _Actions from '../../action/index'
class SimcardContainer extends Component {
componentDidMount(){
const { fetchAPI } = this.props.actions;
**fetchAPI();** // Call API here
}
render() {
const { stateReducer} = this.props;
console.log(stateReducer)
// Here, you will see data that you handled in reducer
// with action type FETCH_SUCCESS
// You should remember data that you fetch from API is asynchronous,
// So you should check like that `data && {do any thing heree}`
return (
<div>
<SimcardList title={"Simcards"} />
<div className="TableNumberItem">{Simcards.SimCardNumber}</div>
<div className="TableNumberItem">{Simcards.SimCardDescription}</div>
<div className="TableNumberItem">{Simcards.TeammatePrice}</div>
</div>
);
}
}
const mapStateToProps = state => ({
stateReducer: state
})
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(_Actions, dispatch)
})
export default connect(mapStateToProps, mapDispatchToProps)(SimcardContainer)
Before Calling createReduxContainer please call createReactNavigationReduxMiddleware so that we know when to trigger your listener
I am trying to integrate react-navigation v3 with redux and this above problem is showing on my simulator
This is a tested sample of react-navigation(v3) and Redux. Here the react-navigation is integrated into Redux. However, I declined to use this method anymore. I believe integrating is not necessary when it is going to make things more complicated.
App.JS file :
import React, {Component} from 'react';
import { Provider } from "react-redux";
import { createStore, applyMiddleware, combineReducers } from "redux";
import { createStackNavigator } from "react-navigation";
import {
reduxifyNavigator,
createReactNavigationReduxMiddleware,
createNavigationReducer
} from "react-navigation-redux-helpers";
import MainScreen from "./Screens/MainScreen";
import SignUpScreen from "./Screens/SignUpScreen";
import rootReducer from "./src/rootReducer";
import ReduxNavigation from "./src/ReduxNavigation";
import { Directions } from 'react-native-gesture-handler';
const AppNavigator = createStackNavigator(
{
// LoginScreen: { screen: LoginScreen },
MainScreen: { screen: MainScreen },
SignUpScreen: { screen: SignUpScreen }
},
{
initialRouteName: "SignUpScreen"
}
);
const navReducer = createNavigationReducer(AppNavigator);
const appReducer = combineReducers({
nav: navReducer,
app: rootReducer
});
const middleware = createReactNavigationReduxMiddleware(
"root",
state => state.nav
);
export const AppNav = reduxifyNavigator(AppNavigator, "root");
const store = createStore(appReducer, applyMiddleware(middleware));
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<Provider store={store}>
<ReduxNavigation />
</Provider>
);
}
}
ReduxNavigation.js :
import React from "react";
import { BackHandler } from "react-native";
import { connect } from "react-redux";
import { NavigationActions } from "react-navigation";
import { AppNav } from "../App";
class ReduxNavigation extends React.Component {
componentDidMount() {
BackHandler.addEventListener("hardwareBackPress", this.onBackPress);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", this.onBackPress);
}
onBackPress = () => {
const { nav, dispatch } = this.props;
if (nav.index === 0) {
return false;
}
dispatch(NavigationActions.back());
return true;
};
render() {
const { nav, dispatch } = this.props;
return <AppNav state={nav} dispatch={dispatch} />;
}
}
const mapStateToProps = state => ({
nav: state.nav
});
export default connect(mapStateToProps)(ReduxNavigation);
this code also support back button feature for Android devices.
mapStateToProps is returning an empty object and then the correct object. This.props.weights is undefined in my component except for inside render
const mapStateToProps = (state) => {
debugger
return {weights: state.fetchWeights}
}
state = {saveWeight: Array(0), fetchWeights: Array(0)}
const mapStateToProps = (state) => {
debugger
return {weights: state.fetchWeights}
}
state = {saveWeight: Array(0), fetchWeights: Array(1)}
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Weight from '../components/Weight.js';
import '../App.css';
class displayWeights extends Component {
constructor(props) {
super(props);
this.state = {weights: this.props.weights};
}
render() {
return (
<div>
</div>
);
}
}
const mapStateToProps = (state) => {
debugger
return {weights: state.fetchWeights}
}
export default connect(mapStateToProps, {})(displayWeights);
-
import fetch from 'isomorphic-fetch';
export function fetchWeights() {
return function(dispatch){
dispatch({type: 'LOADING'})
var url = 'http://localhost:3001/api/v1/weights.json';
var req = new Request(url);
return fetch(req)
.then(function(response) {
return response.json()
})
.then(function(weights) {
debugger
dispatch({type: 'FETCH_WEIGHTS', payload: weights})
})
}
}
export default (state = [], action) => {
switch (action.type) {
case 'FETCH_WEIGHTS':
debugger
return action.payload
default:
return state;
}
}
import { combineReducers } from 'redux';
import saveWeight from './saveWeightReducer.js';
import fetchWeights from './fetchWeightsReducer.js';
export default combineReducers({
saveWeight,
fetchWeights
});
import React, { Component } from 'react';
import logo from './logo.svg';
import { connect } from 'react-redux';
import {
BrowserRouter as Router,
Route
} from 'react-router-dom';
import DisplayWeights from './containers/DisplayWeights.js'
import NewWeight from './containers/NewWeight.js'
import { fetchWeights } from './actions/fetchWeightsAction.js'
import Chart from './components/Chart.jsx';
import './App.css';
class App extends Component {
componentDidMount() {
this.props.fetchWeights()
}
render() {
return (
<div className="App">
<header className="App-header">
<h1>Welcome to Weight Tracker</h1>
<DisplayWeights />
<NewWeight />
<Chart />
</header>
</div>
);
}
}
export default connect(null, {fetchWeights})(App);
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import { store } from './store.js';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
You are triggering the function call to fetchWeights in App component and the response isn't received until the first render of the DisplayWeights components and hence its undefined in the constructor of DisplayWeight. Also you need to assign props to state, if state is directly derivable form props, unless ofcourse you wish to change it locally and then on a submit update it in the props.
I am calling API in action and have to pass those action to reducer. But data from action is not passing to the reducer. I am not using combined-reducer either.
src/index.js is
import React from 'react';
import ReactDOM from 'react-dom';
import PrescriptionIndex from './components/prescriptionIndex.jsx';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<PrescriptionIndex />
</Provider>, document.getElementById("root")
)
This is my component PrescriptionIndex.jsx
import React, { Component } from 'react';
import { index_prescription } from '../action/index.js';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
class PrescriptionIndex extends Component {
constructor(props){
super(props);
this.state={
prescription: null
}
}
action_call(){
index_prescription();
}
render(){
this.state.prescription === null ? this.action_call() : null
return(
<div>
PrescriptionIndex
</div>
)
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({index_prescription}, dispatch)
}
function mapStateToProps( state ){
return {
profiles: state
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PrescriptionIndex);
I am calling index function as index_prescription(); in action_call().
And my action is
import {ADD_PRESCRIPTION} from '../constant.js';
import axios from 'axios';
import dispatch from 'axios';
export const index_prescription = () => {
const base_url= "http://localhost:3000/api/v1/";
const fetch_url = `${base_url}/prescriptions`;
axios.get(fetch_url)
.then(response => {
dispatch({type: ADD_PRESCRIPTION, details: response.data})
})
}
Reducer is
import {ADD_PRESCRIPTION} from '../constant.js';
const profiles = (state=[], action) => {
let profiles = null;
switch(action.type){
case ADD_PRESCRIPTION:
profiles = [...state, action.details]
return profiles;
default:
return state;
}
}
export default profiles;
constant.js is
export const ADD_PRESCRIPTION = "ADD_PRESCRIPTION";
I have verified all the questions, but cant able to study whats going wrong in my code.Thanks in advance.
You miss following code..
import {ADD_PRESCRIPTION} from '../constant.js';
import axios from 'axios';
import dispatch from 'axios';
export const index_prescription = () => {
return dispatch=>{
const base_url= "http://localhost:3000/api/v1/";
const fetch_url = `${base_url}/prescriptions`;
axios.get(fetch_url)
.then(response => {
dispatch({type: "ADD_PRESCRIPTION", details: response.data})
})
}
}
You are calling dispatch from axions? Is not part of react-redux?
If you use redux-promise-middleware you can do this:
import {ADD_PRESCRIPTION} from '../constant.js';
import axios from 'axios';
import dispatch from 'axios';
export const index_prescription = () => {
const base_url= "http://localhost:3000/api/v1/";
const fetch_url = `${base_url}/prescriptions`;
return {
type: "ADD_PRESCRIPTION",
details: axios.get(fetch_url)
.then(response => {
return response;
})
}
}
I am just trying to figure out how to do tests on components that are wrapped with connect. How do I properly define the redux state prop to my component?
● PendingContract with connect/Redux › +++ render the connected(SMART) component
TypeError: Cannot read property 'find' of undefined
Original Component code:
// Dependencies
import React, { Component } from 'react';
import CSSModules from 'react-css-modules';
import { connect } from 'react-redux';
import * as actions from '../../../../actions';
import PendingContractDetail from './pending-contract-
detail/PendingContractDetail';
// CSS
import styles from './PendingContract.css';
export class PendingContract extends Component {
componentWillMount() {
this.props.getSinglePendingContract(this.props.params.contract);
}
render() {
let contract;
if (this.props.contract) {
const contractDetails = this.props.contract;
contract = (
<PendingContractDetail
accepted={contractDetails.accepted}
contractId={contractDetails.contractId}
contractName={contractDetails.contractName}
details={contractDetails.details}
status={contractDetails.status}
timeCreated={contractDetails.timeCreated}
type={contractDetails.type} />
);
} else {
contract = 'Loading...'
};
return (
<div className='row'>
<div className='col-xs-12 col-sm-12 col-md-12'>
{contract}
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
contract: state.pendingContracts.contract
}
}
const PendingContractWithCSS = CSSModules(PendingContract, styles);
export default connect(mapStateToProps, actions)(PendingContractWithCSS);
Test Code as follows:
import React from 'react';
import reduxThunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { shallow, mount } from 'enzyme';
import PendingContract from './PendingContract';
import configureStore from 'redux-mock-store';
jest.mock('react-css-modules', () => Component => Component);
describe('PendingContract with connect/Redux', () => {
const initialState = {
contract: {
accepted: true,
contractId: 1234,
contractName: 'Test Contract',
details: { test: 'test'},
status: 'Accepted',
type: 'Sports'
}
};
const mockStore = configureStore([reduxThunk])
let store,wrapper;
beforeEach(()=>{
store = mockStore(initialState)
wrapper = mount(<Provider store={store}><PendingContract {...initialState} /></Provider>)
})
it('+++ render the connected(SMART) component', () => {
expect(wrapper.find(PendingContract).length).toEqual(1)
});
// it('+++ check Prop matches with initialState', () => {
// expect(wrapper.find(PendingContract).prop('contract')).toEqual(initialState.contract)
// });
});
You need to import connected component if you are trying to fully test it with mount:
import React from 'react';
import reduxThunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { shallow, mount } from 'enzyme';
import ConnectedPendingContract, { PendingContract } from './PendingContract';
import configureStore from 'redux-mock-store';
jest.mock('react-css-modules', () => Component => Component);
describe('PendingContract with connect/Redux', () => {
const initialState = {
contract: {
accepted: true,
contractId: 1234,
contractName: 'Test Contract',
details: { test: 'test'},
status: 'Accepted',
type: 'Sports'
}
};
const mockStore = configureStore([reduxThunk])
let store,wrapper;
beforeEach(()=>{
store = mockStore(initialState)
wrapper = mount(<Provider store={store}><ConnectedPendingContract {...initialState} /></Provider>)
})
it('+++ render the connected(SMART) component', () => {
expect(wrapper.find(PendingContract).length).toEqual(1)
});
// it('+++ check Prop matches with initialState', () => {
// expect(wrapper.find(PendingContract).prop('contract')).toEqual(initialState.contract)
// });
});