I can change the states and update the UI by using Redux. But how to show injected props by Redux on console by using console like console.log(this.props) in run-time. I cannot. I've never seen the props.
Is there a way to show component (class) props -that are assigned from Redux store like the code below-?
function mapStateToProps(state) {
return { iconSize: state.iconSize }
}
function mapDispatchToProps(dispatch) {
return {
setIconSize: (size) => dispatch(setIconSize(size))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Main)
In this example you can rewrite mapStateToProps function:
function mapStateToProps(state) {
const props = { iconSize: state.iconSize };
console.log(props);
return props;
}
It will log props each time this function is invoked.
Related
In my react-redux application, when I mount 2 components together, state of first is being over-ridden by second.
In my first component, I have following state and actions defined:
function mapStateToProps(state) {
return {
comp1: state.comp1,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: {
action1: bindActionCreators(action1, dispatch),
action2: bindActionCreators(action2, dispatch),
}
};
}
And the second component looks something like this:
function mapStateToProps(state) {
return {
comp2: state.comp2,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: {
action3: bindActionCreators(action3, dispatch),
action4: bindActionCreators(action4, dispatch),
}
};
}
But when both the components are finally mounted, only second components state and actions remain. And 1st component's state and action become undefined.
I simply render the components this way:
<Comp1/>
<Comp2/>
Redux version: 3.7.2
React-redux version: 5.0.6
Any help is appreciated. Thanks in advance!
I have this part of my code:
constructor(props) {
super(props)
this.state = {
symbol: '',
side: '',
status: ''
};
this.onInputChange = this.onInputChange.bind(this);
this.onValueChangeSide = this.onValueChangeSide.bind(this);
this.onValueChangeStatus = this.onValueChangeStatus.bind(this);
this.onFormSelect = this.onFormSelect.bind(this);
}
onInputChange(event) {
this.setState({ symbol: event.target.value });
}
onValueChangeSide(event) {
this.setState({ side: event.target.value});
}
onValueChangeStatus(event) {
this.setState({ status: event.target.value});
}
onFormSelect(event) {
this.props.requestAccountsFilter(this.state.symbol, this.state.side,
this.state.status);
}
The requestAccountsFilter is an Action. Its code is:
export function requestAccountsFilter(symbol, side, status) {
return {
type: ACCOUNT_FILTERS,
payload: {
symbol,
side,
status
}
};
}
That approach works fine.
Furthermore, i want to make my component Stateless so i create a container. My problem is that i don't know how to dispatch my action with the above functionality.
I write this:
const MapDispatchToProps = dispatch => (
{
requestAccountsFilter: (symbol, side, status) => {
dispatch(requestAccountsFilter(symbol, side, status));
}
}
);
but it didn't work.
How to dispatch my action in the MapDispatchToProps??
The purpose of mapDispatchToProps is not to actually dispatch actions directly.
mapDispatchToProps is used to bind action creators with dispatch and pass these new bound functions as props to the component.
The main benefit of using mapDispatchToProps is that it makes our code cleaner by abstracting away the store's dispatch method from components. Therefore we can call props that are functions without acces to dispatch like so
this.props.onTodoClick(id);
Whereas if we didn't use mapDispatchToProps then we would have to pass dispatch separately to components and dispatch actions like so:
this.props.dispatch(toggleTodo(id));
You would use mapDispatchToProps as shown in your example code, and then elsewhere write:
mapDispatchToProps must be a pure function and cannot have side effects.
Dispatching actions from inside the function would be considered a side effect. A function has one or more 'side effects' when the evaluation of the function changes state outside of itself (changes global state of app)
Instead use lifecycle hooks to dispatch actions in response to prop changes on components:
class exampleComponent extends Component {
componentDidMount() {
this.props.fetchData(this.props.id)
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.props.fetchData(this.props.id)
}
}
// ...
}
Newbie here trying to learn some Redux.
GOAL: to get a button to click and login/logout, updating the store as true/false status whichever way.
const store = createStore(myReducer)
Created my store, passing in my reducer.
This has a default state of logged out. And returns the opposite, whenever the button is clicked.
I know this action works through debugging.
function myReducer(state = { isLoggedIn: false }, action) {
switch (action.type) {
case 'TOGGLE':
return {
isLoggedIn: !state.isLoggedIn
}
default:
return state
}
}
The problem starts here - when i try to access the store.getState() data.
class Main extends React.Component {
render() {
return (
<div>
<h1>Login Status: { state.isLoggedIn }</h1>
<button onClick={this.props.login}>Login</button>
</div>
)
}
}
const render = () => {
ReactDOM.render(<Main status={store.getState().isLoggedIn} login={() => store.dispatch({ type: 'TOGGLE' })}/>, document.getElementById('root'));
}
store.subscribe(render);
render();
I've tried store.getState().isLoggedIn & store.getState() & this.props.status and then assigning the store.getState().isLoggedIn in the Main component - but nothing works.
Can anyone tell me where i'm going wrong?
You don't directly access the store using getState to find data. The Redux docs explain the process in-depth, but basically you'll connect each component to the Redux store using connect method of the react-redux package.
Here's an example of how this could work for your above component:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Main from '../components/Main'
class MainContainer extends Component {
render() {
return <Main {...this.props} />
}
}
const mapStateToProps = state => ({
isLoggedIn: state.isLoggedIn,
})
const mapDispatchToProps = dispatch => ({
login() {
dispatch({type: 'TOGGLE'})
},
})
MainContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(MainContainer)
export default MainContainer
You would then want to render the MainContainer in place of the Main component. The container will pass down isLoggedIn and login as props to Main when it renders it.
Problem: IDE does not resolve props passed to the component via connect()
Note: this is not a bug, but an inconvenience to the coder
Say I have this React component connected to Redux via connect():
class SomeComponent extends Component {
render() {
return (
{this.props.someObject ? this.props.someObject : ''}
);
}
}
function mapStateToProps(state) {
return {
someObject: new SomeObject(state.someReducer.someObjectInfo),
};
}
function mapDispatchToProps(dispatch) {
return {
// ...
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatsList);
I'm using the IntelliJ IDE, and any prop connected to the component in the above manner, such as someObject, will get an unresolved variable warning. And if someObject has some properties/methods, they will neither be resolved nor show up in code suggestions (which are really helpful).
A workaround
Pass state and dispatch themselves as props:
function mapStateToProps(state) {return {state};}
function mapDispatchToProps(dispatch) {return {dispatch};}
Define my variables in the constructor (as opposed to via props):
constructor(props) {
this.someVar = props.state.someReducer.someVar;
this.someObj = new SomeObject(props.state.someReducer.someObjectInfo;
}
Update the variables manually whenever props change:
componentWillReceiveProps(nextProps) {
someObject.update(nextProps.state.someReducer.someObjectInfo);
}
The drawback is having additional boilerplate logic in componentWillReceiveProps, but now the IDE happily resolves the variables and code suggestion works.
Question
Is the workaround preferable? I'm using it, like it so far, and have not observed any other drawbacks thus far. Is there a better way to get the IDE to understand the code?
Motivation (verbose; only for those interested in why I want to accomplish the above)
The Redux tutorials show a simple way to connect state/dispatch to props, e.g.:
function mapStateToProps(state) {
users: state.usersReducer.users
chats: state.chatsReducer.chats
}
function mapDispatchToProps(dispatch) {
addUser: (id) => dispatch(usersActions.addUser(id))
addChatMsg: (id, msg) => dispatch(chatsActions.addChatMsg(id, msg)
}
In the example above, the coder of a component will need to know every relevant reducers' names and their state variables. This can get messy for the coder. Instead, I want to abstract these details away from the component. One way is with a "module" class that accepts state and dispatch, and provides all get/set methods:
class Chats {
// Actions
static ADD_MESSAGE = "CHATS/ADD_MESSAGE";
constructor(globalState, dispatch) {
this.chatsState = globalState.chats;
this.dispatch = dispatch;
}
// Get method
getChats() {
return this.chatsState.chats;
}
// Set method
addChatMessage(id, msg) {
return this.dispatch({
type: Chats.ADD_MESSAGE,
id,
msg
};
}
// Called by componentWillReceiveProps to update this object
updateChats(nextGlobalState) {
this.chatsState = nextGlobalState.chats;
}
}
Now, if a Component requires the Chats module, a coder simply does this:
class SomeComponent extends Component {
constructor(props) {
this.chats = new Chats(props.state, props.dispatch);
}
componentWillReceiveProps(nextProps) {
this.chats.updateChats(nextProps);
}
// ...
}
And now, all Chats get/set methods and properties will be available, and will be picked up by the IDE.
I think newest Idea can now understand component properties defined via propTypes and provides code completion for them. So you just declare propTypes. And it is not even a workaround, it's a good practice in my opinion.
class ChatsList extends Component {
static propTypes = {
someObject: PropTypes.shape({
color: PropTypes.string,
someFunc: PropTypes.func
}),
someDispatcher: PropTypes.func
};
render() {
return (
{this.props.someObject ? this.props.someObject : ''}
);
}
}
function mapStateToProps(state) {
return {
someObject: new SomeObject(state.someReducer.someObjectInfo),
};
}
function mapDispatchToProps(dispatch) {
return {
someDispatcher: Actions.someDispatcher
// ...
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatsList);
Also, passing the entire state is a bad idea, since a component will receive props and get re-renderend if anything changes in the entire state (unless you provide shouldComponentUpdate)
I've got a component that builds search/sort filters that can be selected. I want the selected state of those filters to be tracked in redux so that the search builder can subscribe and see when they change and update appropriately. the thing I'm trying to figure out how to do (in a way that doesn't feel weird) is populate the filter objects into the state. Eg, right now in the <Search /> component I have something like:
<OptionPicker
group={'searchFilters'}
options={{word: 'price', active: true},
{word: 'distance', active: false},
{word: 'clowns', active: false}}
/>
So how to get those props into state to be used without triggering multiple element renders. I'm also rendering the app on the server as well, so for the initial attachment render, the state already has the options.
In the OptionPicker component I've got:
class OptionPicker extends Component {
constructor(props) {
super(props)
if (!props.optionstate) {
this.props.addOptionState(props)
}
}
render() {
return {this.props.optionstate.word.map((word) => <Option ... />)}
}
}
function mapStateToProps(state, props) {
return {
optionstate: state.optionstate[props.group],
};
}
function mapDispatchToProps(dispatch) {
return {
addOptionState: (props) => {
dispatch(addOptionState(props));
},
optionToggled: (group, word) => {
dispatch(updateOptionState(group, word));
}
};
}
export default connect(mapStateToProps,mapDispatchToProps)(OptionGroup);
This kinda works, but there exists a time when render is called before the redux state has been populated, which throws an error. I could guard against that, but none of this feels "right". Shouldn't that prop always be there? Is there a pattern for this that I'm missing?
I agree with you in that the prop should always be there. The pattern I use for this is to set up an initial state and to pass it to the reducer function:
export const INITIAL_STATE = {
optionstate: { /* all filters are present but deactivated */ }
};
export default function (state = INITIAL_STATE, action) {
// reduce new actions into the state
};