I am new to redux and react both. I am trying out my first redux app where my state is getting passed as undefined. Although I searched around the internet to figure the issue but couldn't find out whats the problem.
Below are the code snippets -
Init.js
export default function() {
return {
isLoggedIn: false,
username: "admin"
};
}
Reducer.js
import initialState from "./init";
let currentState = initialState;
export default function(state = currentState, action) {
if (action.type === "ONLICK") {
return state;
} else {
return state;
}
}
Index.js
const allReducers = combineReducers({
currentstate: currentState
});
export default allReducers;
currentState.js
import React, { Component } from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { allreducers } from "./../reducer/index";
class CurrentState extends Component {
render() {
console.log("Logged In-" + this.props.isLoggedIn);
return <div>{this.props.isLoggedIn}</div>;
}
}
function mapStateToProps(state) {
return {
isLoggedIn: state.isLoggedIn
};
}
export default connect(mapStateToProps)(CurrentState);
navbar.js
import React, { Component } from "react";
import CurrentState from "./../container/currentstate";
import "bootstrap/dist/css/bootstrap.css";
class Navbar extends Component {
constructor() {
super();
}
render() {
return (
<div>
<h3>State -</h3>
<CurrentState />
</div>
);
}
}
Navbar.js is where I am displaying the one of the attributes of the state. But in the console when I am trying to print the value it is getting displayed as undefined.
You're getting state in wrong way in mapStateToProps
Use
function mapStateToProps(state) {
return {
isLoggedIn: state.currentstate
};
}
instead
function mapStateToProps(state) {
return {
isLoggedIn: state.isLoggedIn
};
}
Hope so it will help
Related
I am trying to get my categoryName from initialState but it says "TypeError: Cannot read property 'categoryName' of undefined problem". How can I solve this case?
here is my initialState.js file:
export default {
currentCategory:{categoryName:"Beverages"},
categories:[]
};
And here is my categoryList.js file:
import React, { Component } from "react";
import { connect } from "react-redux";
import {bindActionCreators} from 'redux'
import * as categoryActions from '../../redux/actions/categoryActions'
class CategoryList extends Component {
componentDidMount(){
this.props.actions.getCategories()
}
render() {
return (
<div>
<h3>Categories {this.props.categories.legnth}</h3>
<h5>Seçili Kategori: {this.props.currentCategory.categoryName}</h5>
</div>
);
}
}
function mapStateToProps(state) {
return {
currentCategory: state.changeCategoryReducers,
categories:state.categoryListReducer
}
}
function mapDispatchToProps(dispatch){
return {
actions:{
getCategories:bindActionCreators(categoryActions.getCategories,dispatch)
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(CategoryList);
I am using reducers , redux and thunk but I have a problem I think should be install problem. But I cant solve which package is missed. For any advice will be very effective.
And here is my changeCategoryReducer.js:
import * as actionTypes from "../actions/actionTypes";
import initialState from "./initialState";
export default function changeCategoryReducer(
state = initialState.currentCategory,
action
) {
switch (action.type) {
case actionTypes.CHANGE_CATEGORY:
return action.payload;
default:
return state;
}
}
here is my index.js file:
import { combineReducers } from "redux";
import changeCategoryReducer from "./changeCategoryReducer";
import categoryListReducer from './categoryListReducer';
const rootReducer = combineReducers({
changeCategoryReducer,
categoryListReducer,
});
export default rootReducer;
I didn't see this line: currentCategory: state.changeCategoryReducers,
But #Tauseef Ahmad saw.
It should be like this: currentCategory: state.changeCategoryReducer,
I encountered this problem when I was testing my newly created action and reducer. The prop is not being updated even though I'm setting it to a fixed value within my reducer.
Component:
class <ComponentName> extends Component {
componentDidMount() {
login()
}
render() {
if(this.props.isLogged)
return (
<App/>
);
else
return (
<ErrorScreen/>
);
}
}
function mapStateToProps(state) {
return {
isLogged:state.auth.isLogged
}
}
const mapDispatchToProps = (dispatch) => {
return {
login: () => dispatch(login())
};
};
export default connect(mapStateToProps,mapDispatchToProps)(<ComponentName>)
Action:
export function login() {
return {
type:"TEST"
}
}
Reducer:
const initState = {
isLogged: false,
}
export default (state=initState, action) => {
switch(action.type) {
case "TEST":
return {
...state,
isLogged: true
}
break;
default:
return state
}
}
Combine Reducer:
import {combineReducers} from 'redux'
import AuthenticationReducer from './authenticationReducer'
export default combineReducers({
auth: AuthenticationReducer
})
Provider:
import React, {Component} from "react";
import <ComponentName> from './app/screens/<ComponentName>'
import store from './app/store'
import {Provider} from 'react-redux'
export default () =>
<Provider store={store}>
<<ComponentName>/>
</Provider>;
Been trying to debug this for some time now. I still don't know why this is happening. Maybe I implemented it wrongly? If there are some files I forgot to include, please inform me. Thanks and have a nice day!
The reason your code isn't working as expected is because you're calling the login() action creator, rather than the login() method that is returned from mapDispatchToProps() (and injected into the props of <ComponentName/>).
Try revising your code by adding this.props before your call to login() like so:
class <ComponentName> extends Component {
componentDidMount() {
// Update this line here so that the login() method
// injected by connect() is called (ie via this.props)
this.props.login()
}
render() {
if(this.props.isLogged)
return <App/>
else
return <ErrorScreen/>
}
}
For example: i have 2 controll-view container user.cv.jsx and sidebar.cv.jsx
Screen consist of User and Sidebar. Sidebar rendering in User screen.
User container:
import React from 'react'
import {Link} from 'react-router-dom';
import { connect } from 'react-redux';
import UserTypeComponents from '../components/user_type.jsx'
import Sidebar from '../../sidebar/containers/sidebar.cv.js'
import * as showList from '../action/list.action.js';
import * as userLimit from '../action/limit.action.js';
import PropTypes from 'prop-types'
function mapStateToProps (state) {
return {...state}
}
class UserType extends React.Component {
constructor (props, context) {
super(props);
this.context = context;
if(!this.props.oauth.isAuthenticating) {
this.context.router.history.push('/login');
return;
}
}
componentDidMount() {
}
render() {
console.log(this.props);
return (<div>
<Sidebar />
<UserTypeComponents {...this.props} />
</div>);
}
}
UserType.contextTypes = {
router: PropTypes.object
}
export default connect(mapStateToProps)(UserType);
And Sidebar Container:
import React from 'react'
import {Link} from 'react-router-dom';
import ShowSidebar from '../components/sidebar.jsx';
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import Preloader from '../../../helpers/preloader.helper.js'
import * as active from '../action/active.action.js'
import * as list from '../action/list.action.js'
import * as show from '../action/show.action.js'
import {DEFAULT_COMPONENTS} from '../constant/sidebar.const.js';
function mapStateToProps (state) {
return state.sidebar
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
...active,
...list,
...show
}, dispatch);
}
class Sidebar extends React.Component {
constructor (props) {
super(props);
}
listOfLinks(){
const makeRequest = async () => {
try {
const data = await (await fetch('http://localhost:3000/sidebar')).json(),
active = this.activeComponent(data);
this.props.list(data);
this.props.active(active);
} catch (err) {
console.log(err)
}
}
makeRequest()
}
activeComponent(data){
for(let key of data){
if(location.pathname.indexOf(key.name.toLowerCase()) != -1){
return key.name.toLowerCase();
}
}
return DEFAULT_COMPONENTS;
}
componentWillMount() {
this.listOfLinks();
}
activeSidebarState(event){
let parent = event.target.parentNode,
target = _$('.site-sidebar__name', parent),
text = target.innerText.toLowerCase();
this.props.active(text);
}
render() {
const loading = this.props.sidebar.links.length;
return (loading ? <ShowSidebar changeActive={::this.activeSidebarState} active={this.props.sidebar.active} links={this.props.sidebar.links} /> : <Preloader />);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Sidebar);
For all this, action and redusers are written. The sidebar sends a request to the server and requests all the modules and forms links to them, too. The user module is accessing the server and requires all users. The problem is that the preloader is being formed in the sidebar, and when the sidebar is loaded the preloader disappears. But the users still could not boot.
The question is: How to control the loading of the sidebar and the user, so that when these two components are updated, the state remove the preloader.
A common practice is to store isFetching flag in the reducer and update it in respond to fetch actions. For example:
function users(state = { users: [], isFetching: false }, action) {
switch (action.type) {
case 'FETCH_USERS_START':
return { ...state, isFetching: true };
case 'FETCH_USER_SUCCESS':
return { ...state, isFetching: false, users: action.payload.users };
default:
return state;
}
}
Then you can access it from both your components via mapStateToProps and show the preloader.
A main thing here is that you need to move the async call to an action, so reducer will be able to react to it. You can use redux-thunk middleware.
I'm new to react and I'm trying to understand on to make async ajax request. I was able to get the request completed and the data returned added to my state, but I can't render the data to my component. Here's my setup.
UserPage component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { getUsers } from '../../actions';
class User extends Component {
displayName: 'User';
componentWillMount() {
this.props.getUsers();
}
renderUsers() {
return this.props.users.map(user => {
return (
<h5>{user.Name}</h5>
);
});
}
render() {
var component;
if (this.props.users) {
component = this.renderUsers()
} else {
component = <h3>asdf</h3>;
}
return (
<div>
{component}
</div>
);
};
};
function mapStateToProps(state) {
return {
users: state.all
};
}
export default connect(mapStateToProps, { getUsers })(User);
Action
import request from '../helpers/request';
import { GET_USERS } from './types';
export function getUsers() {
return request.get(GET_USERS, 'Person/GetPeople');
}
With the get function from request.js module
get: function (action, url) {
return (dispatch) => {
axios.get(`${ROOT_URL}${url}`)
.then(({ data }) => {
dispatch({
type: action,
payload: data
});
});
};
}
User_reducer
import { GET_USERS } from '../actions/types';
export default function (state = {}, action) {
switch (action.type) {
case GET_USERS:
console.log(action);
return { ...state, all: action.payload }
default:
return state;
};
}
When I load the UserPage component, I can see the request being done and the state being updated in the redux dev tool, but I can't display this.props.users.
If I take out the if(this.props.users) in the render() method of the User component, I get an undefined on this.props.users.
What am I doing wrong?
Any help greatly appreciated! Thank you
SOLVED
The solution was to set the users property to state.users.all in the mapStateToProps function.
function mapStateToProps(state) {
return {
users: state.users.all
};
}
Thank you
Could you check, do you have this.renderUsers() defined in that render function? It might be you forgot to bind it, and that function is undefined?
Try to add this.renderUsers = this.renderUsers.bind(this); in your constructor and see, if it helps.
I have this autocomplete component that takes an array of terms as a dataSource prop. The data I want to feed in resides in a public API, and I've followed the tutorial here to get to the code below. But this tutorial (and many others out there) explain how to bind these actions to an event, whereas I want to populate this prop with data on page load. How would I go about doing that?
actions.js
import fetch from 'isomorphic-fetch';
export function loadSchools(termId) {
return {
type: 'LOAD_SCHOOLS',
termId
};
}
export function receiveSchools(termId, json) {
return {
type: 'RECEIVE_SCHOOLS',
termId,
schools: json.data.children.map(child => child.data), // ???
receivedAt: Date.now()
};
}
export function getSchools(termId) {
return function (dispatch) {
dispatch(loadSchools(termId));
return fetch('http://www.northwestern.edu/class-descriptions/4650/index-v2.json')
.then(response => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => dispatch(receiveSchools(termId, data)));
};
}
reducers.js
const initialState = {
schoolsData: {
isFetching: false,
lastUpdated: 0,
schools: []
}
};
function schools(state = initialState, action) {
switch (action.type) {
case 'LOAD_SCHOOLS':
return {
...state,
isFetching: true
};
case 'RECEIVE_SCHOOLS':
return {
...state,
isFetching: false,
schools: action.schools,
lastUpdated: receivedAt
}
default:
return state;
}
}
export default schools;
Search.jsx
import React from 'react';
import AutoComplete from 'material-ui/AutoComplete';
export default class Search extends React.Component {
render() {
return (
<AutoComplete
hintText="Search for something."
dataSource={this.props.searchdata}
maxSearchResults={15}
filter={AutoComplete.caseInsensitiveFilter}
onNewRequest={}
/>
);
}
}
Search.propTypes = {
searchdata: React.PropTypes.array.isRequired,
onSelect: React.PropTypes.func
};
index.jsx
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import { grey500, white, fullBlack } from 'material-ui/styles/colors';
import { fade } from 'material-ui/utils/colorManipulator';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import schools from './reducers/reducers';
import colors from './colors';
import NavBar from './components/NavBar.jsx';
import Serif from './components/Serif.jsx';
const store = createStore(schools, applyMiddleware(thunkMiddleware));
const muiTheme = getMuiTheme({
palette: {
primary1Color: colors.northwesternPurple,
primary2Color: colors.northwesternPurple120,
primary3Color: grey500,
accent1Color: colors.northwesternPurple30,
accent2Color: colors.richBlack10,
accent3Color: colors.richBlack50,
textColor: colors.richBlack80,
alternateTextColor: white,
canvasColor: white,
borderColor: colors.richBlack20,
disabledColor: fade(colors.richBlack80, 0.3),
pickerHeaderColor: colors.northwesternPurple,
clockCircleColor: fade(colors.richBlack80, 0.07),
shadowColor: fullBlack
}
});
class App extends React.Component {
render() {
return (
<Provider store={store}>
<MuiThemeProvider muiTheme={muiTheme}>
<div> {/* MuiThemeProvider requires stricly one child element */}
<NavBar />
<Serif /> {/* This component contains SearchContainer, which in turn contains Search */}
</div>
</MuiThemeProvider>
</Provider>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
You can render your Search component from another component, let's call it SearchContainer. SearchContainer is decorated by the connect function from react-redux that has as only role to dispatch the action to fetch the schools. SearchContainer doesn't render Search component until the school are fetched.
Here an example of what the code would look like. Here I assume you don't use react-redux.
First you have a small problem in your initial state in reducers.js. It should be:
const initialState = {
isFetching: false,
lastUpdated: 0,
schools: []
};
function schools(state = initialState, action) {
switch (action.type) {
case 'LOAD_SCHOOLS':
return {
...state,
isFetching: true
};
case 'RECEIVE_SCHOOLS':
return {
...state,
isFetching: false,
schools: action.schools,
lastUpdated: receivedAt
}
default:
return state;
}
}
SearchContainer.js
// ./containers/SearchContainer.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loadSchools } from '../actions/actions'
import Search from '../components/Search';
class SearchContainer extends Component {
componentDidMount() {
this.props.loadSchools(this.props.termId);
},
render() {
const {
schools,
isFetching
} = this.props;
if (isFetching) {
return null;
}
return <Search schools={schools} />;
}
}
const mapStateToProps = (state) => ({
isFetching: state.isFetching,
schools: state.schools
});
const mapActionsToProps = (dispatch) => ({
loadSchools: (termId) => dispatch(loadSchools(termId)),
});
export default connect(mapStateToProps, mapActionsToProps)(SearchContainer);
In this way, at the first render, your Search component is not rendered. It is rendered, only after the schools are loaded.
You can dispatch the LOAD_SCHOOLS action from the componentDidMount lifecycle method (maybe in your Serif component but I can't see the code for that).
From the docs:
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.
https://facebook.github.io/react/docs/react-component.html#componentdidmount