I am new to using Redux and cannot figure out why my Sidebar component is not being updated to open the drawer. I can see that my action is getting called but it doesn't seem to trigger any update in my component. I have tried to follow the Redux documentation and I can't see what I am missing as I have set my code up the same way.
I didn't want to fill the post up with code but let me know if you need any more information to help solve this. I'd appreciate any help, thanks.
Solution: I was using the wrong property on the state object so my component wasn't being updated. It should have been state.Sidebar.open not state.open.
const mapStateToProps = (state:any) => ({
open: state.Sidebar.open
});
Menu container:
import { connect } from 'react-redux';
import { toggleSidebar } from '../actions';
import Menu from '../components/Menu/Menu';
const mapStateToProps = (state:any) => ({
open: state.open ? state.open : false
});
const mapDispatchToProps = (dispatch:any) => ({
toggleSidebar: (open:boolean) => dispatch(toggleSidebar(open))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Menu);
Menu component:
class Menu extends React.Component<IMenuProps> {
private handleDrawerOpen = () => {
this.props.toggleSidebar(true);
}
private handleDrawerClose = () => {
this.props.toggleSidebar(false);
}
public render() {
return (
<div>
<MenuBar
handleDrawerOpen={this.handleDrawerOpen}
/>
<SideBar
open={this.props.open}
handleDrawerClose={this.handleDrawerClose}
/>
</div>
);
}
}
Sidebar:
class Sidebar extends React.Component<ISideBarProps> {
public render() {
return (
<div>
<Drawer
open={this.props.open}
onClose={this.props.handleDrawerClose}>
<div
tabIndex={0}
role="button"
>
<AdministrationItems />
</div>
</Drawer>
</div>
);
}
}
*Edit
Reducer:
const sidebar = (state = { open: false }, action:any) => {
switch (action.type) {
case TOGGLE_SIDEBAR: {
return {
open: action.open
};
}
default: {
return state;
}
}
};
Action:
export const toggleSidebar = (open:boolean) => ({
open,
type: TOGGLE_SIDEBAR,
});
I believe the error is in the reducer. Try replacing the reducer with:
const sidebar = (state = { open: false }, action:any) => {
switch (action.type) {
case TOGGLE_SIDEBAR: {
return {
...state,
open: action.open
};
}
default: {
return state;
}
}
};
You should always spread the state for every case. Let me know if it worked
Solution: I was using the wrong property on the state object so my component wasn't being updated. It should have been state.Sidebar.open not state.open.
const mapStateToProps = (state:any) => ({
open: state.Sidebar.open
});
Related
I'm learning React-Redux, and I'm trying to build an app in which I write an input called trial in LIST CREATION, insert it in list and through the function updateList update the initial state of list in the REDUCER, and then display that list in the TRIALLIST screen, since nothing really happened when I launched the code, I've put a console.log in the INDEX.JS and it returns the error Actions must be plain objects. Instead, the actual type was: 'undefined'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions., I've tried googling it but I could not seem to find a solution. Thank you in advance for your help.
LIST CREATION
import { connect } from 'react-redux';
import { updateList } from '../../../../../redux/actions/index.js';
class trial extends Component {
constructor(props) {
super(props);
this.state = {
trial: '',
list: [],
};
}
submitTrial(){
let list = this.state.list;
list.push(this.state.trial);
this.props.updateList(list);
this.props.navigation.navigate("TrialList");
}
render() {
return (
<Button transparent>
<Icon
name="checkmark"
onPress={() => this.submitTrial()}
/>
</Button>
<TextInput
placeholder='type here'
onChangeText={(trial) => this.setState({ trial })}
/>
const mapDispatchToProps = { updateList };
export default connect( mapDispatchToProps )( trial );
TRIALLIST
class TrialList extends Component {
constructor(props) {
super(props);
this.state = {
list: this.props.list,
};
}
render() {
return (
<FlatList
data={this.state.list}
renderItem={({ item }) => (
//some data ///
/>
function mapStateToProps(store){
return{
list: store.userState.list
};
}
export default connect(mapStateToProps)(TrialList);
INDEX.JS
import { ADD_LIST } from "../constants/index";
export const updateList = (list) => {
return console.log({ type: ADD_LIST, payload: list}) <----------------------HERE
}
REDUCER
import { USER_STATE_CHANGE, ADD_LIST } from "../constants";
const initialState = {
currentUser: null,
list: [],
};
export const user = (state = initialState, action) => {
switch (action.type){
case USER_STATE_CHANGE:
return {
...state,
currentUser: action.currentUser,
};
case ADD_LIST:
return{
...state,
list: [...action.payload],
}
default:
return state
}
};
APP.JS
const store = createStore(rootReducer, applyMiddleware(thunk));
export class App extends React.Component {
render() {
return (
<Provider store={store}>
<TrialList/>
<trial/>
<Provide/>
Why are you returning console.log here?
export const updateList = (list) => {
return console.log({ type: ADD_LIST, payload: list}) <----------------------HERE
}
console.log returns undefined, and, if you want to log the action triggered, then, console.log it first and then return the object like this.
export const updateList = (list) => {
console.log({ type: ADD_LIST, payload: list}) <----------------------HERE
return { type: ADD_LIST, payload: list}
}
export const updateList = (list) => ({
type: ADD_LIST, payload: list
})
You are missing the parenthesis around after the arrow and before closing the function body.
Context
The goal is to have a component with a key name being react-rendered in App.js when I press a specific key, registered in another component. The information is being passed thorugh a redux managed state.
The problem
It's simple :
I'm updating my state in my redux reducer but even when duplicating it (I can see it thanks to the redux dev tool that allows me to watch my prevState and my nextState being different)
And the question is as simple :
Why my App.js component won't re-render even after connecting to and
duplicating my state ?
I think I made sure that my state was duplicated with the spreading operation and my redux dev tool display me a good state update without having my prevState and nextState duplicated. I looked through a lot of posts and found only people that forgot to duplicate their state in their reducers, which I did not.
So what's the problem here ??
DevTool Sample
Code
Here is the code, quite simple. The interesting piece is playSound and playedKeys:
App.js :
import React from 'react'
import './App.css';
import { connect } from 'react-redux';
import KeyComponent from './Components/Key'
import SoundPlayer from './Components/Sounds'
const mapStateToProps = (state) => ({
...state.soundReducer
})
class App extends React.Component {
constructor(props) {
super(props);
}
render(){
return (
<div>
{console.log(this.props)}
{
this.props.playedKeys.map(key =>{
<KeyComponent keyCode={key}> </KeyComponent>
})
}
<SoundPlayer></SoundPlayer>
</div>
);
}
}
export default connect(mapStateToProps)(App);
Reducer
export default (state = {allSounds:{},playedKeys:[]}, action) => {
switch (action.type) {
case 'ADD_SOUND':
return reduce_addSound({...state},action)
case 'PLAY_SOUND':
return reduce_playSound({...state,playedKeys : [...state.playedKeys]},action)
default:
return state
}
}
function reduce_addSound (state,action){
let i = 0
state.allSounds[action.payload.key] = { players : new Array(5).fill('').map(()=>(new Audio())) , reader : new FileReader()}
//load audioFile in audio player
state.allSounds[action.payload.key].reader.onload = function(e) {
state.allSounds[action.payload.key].players.forEach(player =>{
player.setAttribute('src', e.target.result);
player.load();
player.id = 'test'+e.target.result+ i++
})
}
state.allSounds[action.payload.key].reader.readAsDataURL(action.payload.input.files[0]);
return state
}
function reduce_playSound(state,action){
state.playedKey = action.payload.key;
if(!state.playedKeys.includes(state.playedKey))
state.playedKeys.push(action.payload.key);
return state
}
Action
export const addSound = (key, input,player) => (dispatch,getState) => {
dispatch({
type: 'ADD_SOUND',
payload: {key : key, input : input}
})
}
export const playSound = (key) => (dispatch,getState) => {
dispatch({
type: 'PLAY_SOUND',
payload: {key : key}
})
}
The component registering the keypresses
import React from 'react'
import { connect } from 'react-redux';
import { playSound } from '../../Actions/soundActions';
const mapStateToProps = (state) => ({
...state.soundReducer
})
const mapDispatchToProps = dispatch => ({
playSound: (keyCode) => dispatch(playSound(keyCode))
})
class SoundPlayer extends React.Component {
constructor(props) {
super(props);
}
componentDidMount () {
this.playSoundComponent = this.playSoundComponent.bind(this)
document.body.addEventListener('keypress', this.playSoundComponent);
}
keyCodePlayingIndex = {};
playSoundComponent(key){
if(this.props.allSounds.hasOwnProperty(key.code)){
if(!this.keyCodePlayingIndex.hasOwnProperty(key.code))
this.keyCodePlayingIndex[key.code] = 0
this.props.allSounds[key.code].players[this.keyCodePlayingIndex[key.code]].play()
this.keyCodePlayingIndex[key.code] = this.keyCodePlayingIndex[key.code] + 1 >= this.props.allSounds[key.code].players.length ? 0 : this.keyCodePlayingIndex[key.code] + 1
console.log(this.keyCodePlayingIndex[key.code])
}
this.props.playSound(key.code);
}
render(){
return <div>
<h1 >Played : {this.props.playedKey}</h1>
{Object.keys(this.keyCodePlayingIndex).map(key =>{
return <p>{key} : {this.keyCodePlayingIndex[key]}</p>
})}
</div>
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SoundPlayer);
Issue
You are mutating your state object.
state.allSounds[action.payload.key] = ...
state.playedKey = action.payload.key;
Solution
Update your reducer functions to return new state objects, remembering to correctly shallow copy each level of depth that is being updated.
export default (state = { allSounds: {}, playedKeys: [] }, action) => {
switch (action.type) {
case 'ADD_SOUND':
return reduce_addSound({ ...state },action);
case 'PLAY_SOUND':
return reduce_playSound({ ...state, playedKeys: [...state.playedKeys] }, action);
default:
return state
}
}
function reduce_addSound (state, action) {
const newState = {
...state, // shallow copy existing state
allSounds: {
...state.allSounds, // shallow copy existing allSounds
[action.payload.key]: {
players: new Array(5).fill('').map(()=>(new Audio())),
reader: new FileReader(),
},
}
};
// load audioFile in audio player
newState.allSounds[action.payload.key].reader.onload = function(e) {
newState.allSounds[action.payload.key].players.forEach((player, i) => {
player.setAttribute('src', e.target.result);
player.load();
player.id = 'test' + e.target.result + i // <-- use index from forEach loop
})
}
newState.allSounds[action.payload.key]
.reader
.readAsDataURL(action.payload.input.files[0]);
return newState;
}
function reduce_playSound (state, action) {
const newState = {
...state,
playedKey: action.payload.key,
};
if(!newState.playedKeys.includes(newState.playedKey))
newState.playedKeys = [...newState.playedKeys, action.payload.key];
return newState
}
Okay I've got it, it's always the simplest stupidest thing that we don't check huh.
Clarification
So my state was properly duplicated with reduce_addSound({ ...state },action) and reduce_playSound({ ...state, playedKeys: [...state.playedKeys] and like I wrote in my question, that wasn't the issue !
Issue
As old as it can get, I wasn't returning a component in my render function.. :
in App.js :
render(){
return (
<div>
{
this.props.soundReducer.playedKeys.map(key =>{
<KeyComponent keyCode={key}> </KeyComponent> //<-- NO return or parenthesis !!
})
}
<SoundPlayer></SoundPlayer>
</div>
);
}
Answer
App.js render function with parenthesis:
render(){
return (
<div>
{
this.props.soundReducer.playedKeys.map(key =>(
<KeyComponent key = {key} keyCode={key}> </KeyComponent> //<-- Here a component is returned..
))
}
<SoundPlayer></SoundPlayer>
</div>
);
}
So, i wrote a test project to explore react, react-router and react-redux.
After i got everything working fine i laid my eyes again on Settings.jsx and i am wondering how could i make it less verbose and error prone:
import React, { Component } from "react";
import { connect } from "react-redux";
class Settings extends Component {
state = { name: this.props.settings.name };
render() {
return (
<div>
<h1>Settings</h1>
<p>This is Settings page</p>
My name is{" "}
<input
value={this.state.name}
onChange={e => this.setState({ name: e.target.value })}/>
<button onClick={e => this.props.changeName(this.state.name)}>
Change
</button>
</div>
);
}
}
const mapState = state => ({ settings: state.settings });
const mapDispatch = dispatch => {
return {
changeName(name) {
dispatch({ type: "setName", name });
}
};
};
export default connect(
mapState,
mapDispatch
)(Settings);
My first idea was to convert it into a functional component, but it's said that they don't have state and i need the state to locally handle the input.
With #babel/plugin-proposal-decorators, connect can be used as a decorator:
import React, { Component } from "react";
import { connect } from "react-redux";
const mapState = state => ({ settings: state.settings });
const mapDispatch = dispatch => {
return {
changeName(name) {
dispatch({ type: "setName", name });
}
};
};
#connect(mapState, mapDispatch)
export default class Settings extends Component {
state = { name: this.props.settings.name };
render() {
return (
<div>
<h1>Settings</h1>
<p>This is Settings page</p>
My name is{" "}
<input
value={this.state.name}
onChange={e => this.setState({ name: e.target.value })}/>
<button onClick={e => this.props.changeName(this.state.name)}>
Change
</button>
</div>
);
}
}
small, but imho nice simplification
also, you could use concise syntax with your mapDispatch:
const mapDispatch = dispatch => ({
changeName(name) {
dispatch({ type: "setName", name });
}
});
you can do this if you want to to add the typing text in store:
Settings.js
import React from "react";
import { changeName, typingName } from '../actions/settingsActions'
import { connect } from "react-redux";
const Settings = () => {
const { changeName, typingName, typedName, submittedName } = this.props
return (
<div>
<h1>Settings</h1>
<p>This is Settings page</p>
My name is{" "}
<input
value={typedName}
onChange={e => typingName(e.target.value)}/>
<button onClick={changeName(submittedName)}>
Change
</button>
</div>
);
}
const mapState = state => ({
typedName: state.typedName,
submittedName: state.submittedName
});
const mapDispatchToProps = dispatch => ({
typingName: x => dispatch(typingName(x)),
changeName: x => dispatch(changeName(x))
})
export default connect(
mapState,
mapDispatch
)(Settings);
settingsActions.js
export const typingName = payload => ({
type: 'TYPING_NAME',
payload
});
export const changeName = payload => ({
type: 'CHANGE_NAME',
payload
});
settingsReducer.js
export const typingName = (state = [], action) => {
switch (action.type) {
case 'TYPING_NAME':
return [...state, action.payload];
default:
return state;
}
};
export const changeName = (state = '', action) => {
switch (action.type) {
case 'CHANGING_NAME':
return action.payload;
default:
return state;
}
};
You could maybe achieve something like this. But validating the typing state inside the component then sending the final result to the store as you did is a better idea I think, to avoid so much verbose.
Also you should of course create a constants file, but I guess you know already.
So inside of my uncontrolled PossibleMatches component, I know from the way React works, the initial rendering phase will occur with empty prop values (if those prop values rely on external application state (mapStateToProps)) regardless of whether or not I have a componentDidMount lifecycle method or constructor setup. In response to this, I've setup a promise inside of the componentDidMount so that when I dispatch prop functions [defaultPieces, arrangePieces], I can have the UI render an ActivityIndicator to indicate something is currently fetching. The problem is, I cannot seem to get the mapStateToProps function to understand the state when I call mapStateToProps from within the success phase of the promise. Here it is:
class PossibleMatches extends Component {
constructor(props){
super(props);
}
componentDidMount(props){
return new Promise((resolve, reject) => {
let state;
let {defaultPieces, arrangePieces, isFetching} = this.props;
let makeClothesAppear = function(){
defaultPieces();
arrangePieces();
isFetching = true;
}
resolve(makeClothesAppear());
}).then(function(state){
mapStateToProps(state);
this.props.isFetched = true
this.props.isFetching = false;
}).catch((error) => {
console.log('FetchClothesError: ', error);
})
}
}
How the UI would make a decision on what to display:
renderDecision(){
const {UpperComponents, LowerComponents} = this.props;
const {currentUpperComponent, currentLowerComponent} = this.state.currentComponent.whichPiece;
const {LowerComponentEnabled, UpperComponentEnabled} = this.state;
if (this.props.isFetching){
return (<div className='activityLoader'>
<ActivityIndicator number={3} duration={200} activeColor="#fff" borderWidth={2} borderColor="50%" diameter={20}/>
</div>);
} else if (this.props.isFetched){
return (<div className = "PossibleMatches_Container">
<i className = 'captureOutfit' onClick = {this.snapshotMatch}></i>
{UpperComponents.map((component) => {
return (<UpperComponent key={component.createdAt} id={component.id}
switchComponent={this.switchFocus}
setCurrentPiece = {this.setNewPiece}
evaluatePiece={this.isOppositeComponentSuggested}
image={component.image}
toggleToPiece = {(LowerComponentEnabled) => {if (LowerComponentEnabled === false){this.setState({LowerComponentEnabled: true})}else{return;} this.setState({currentLowerComponent: this.props.suggestedBottoms[0]})}}
isLowerComponentEnabled={LowerComponentEnabled}
ref={this.residingUpperComponent}
className = {this.state.currentComponent.whichPiece.whichType === 'match' ? 'PossibleMatches_Container' : this.state.currentComponent.whichPiece.whichType === 'bottom' ? 'standalonePiece' : 'standalonePiece'}/>)
})
}
{LowerComponents.map((component) => {
return (<LowerComponent key={component.createdAt} id={component.id}
setCurrentPiece = {this.setNewPiece}
evaluatePiece={this.isOppositeComponentSuggested}
image={component.image}
toggleToPiece={(UpperComponentEnabled) => {if (UpperComponentEnabled === false){this.setState({UpperComponentEnabled: true})}else{return;} this.setState({currentUpperComponent: this.props.suggestedTops[0]})}}
switchComponent={this.switchFocus}
isUpperComponentEnabled={UpperComponentEnabled}
ref={this.residingLowerComponent}
className = {this.state.currentComponent.whichPiece.whichType === 'match' ? 'PossibleMatches_Container' : this.state.currentComponent.whichPiece.whichType === 'bottom' ? 'standalonePiece' : 'standalonePiece'}/>)
})
}
</div>)
}
}
render(){
return(
<div className = 'GorClothingContainer'>
{/*<Wardrobe upperComponent={this.state.currentComponent.whichPiece.currentUpperComponent} lowerComponent={this.state.currentComponent.whichPiece.currentLowerComponent} enableCapture={(snapshot) => this.snapshotMatch = snapshot} />*/}
{this.renderDecision()}
</div>
);
}
My PossibleMatches Reducer
import {INITIAL_PIECES, GET_ANCILLARY_PIECES, ORGANIZE_PIECES, SET_CONTEMPLATED_PIECE} from '../actions/types';
const initialState = {
UpperComponents: [],
LowerComponents: [],
contemplated_piece: null,
extraTops: [],
extraBottoms: [],
standaloneTops: [],
standaloneBottoms: [],
suggestedTops: [],
suggestedBottoms: []
}
export default function(state = initialState, action){
switch(action.type){
case INITIAL_PIECES:
return Object.assign({}, state, {contemplated_piece: action.payload.contemplated_piece},
{extraTops: action.payload.extra_tops},
{extraBottoms: action.payload.extra_bottoms},
{standaloneTops: action.payload.standalone_tops},
{standaloneBottoms: action.payload.standalone_bottoms},
{suggestedTops: action.payload.suggested_tops},
{suggestedBottoms: action.payload.suggested_bottoms})
case GET_ANCILLARY_PIECES:
return Object.assign({}, state, {extraTops: action.payload.extra_tops},
{extraBottoms: action.payload.extra_bottoms},
{standaloneTops: action.payload.standalone_tops},
{standaloneBottoms: action.payload.standalone_bottoms},
{suggestedTops: action.payload.suggested_tops},
{suggestedBottoms: action.payload.suggested_bottoms})
case ORGANIZE_PIECES:
return Object.assign({}, state, {UpperComponents: action.payload.UpperComponents},
{LowerComponents: action.payload.LowerComponents})
case SET_CONTEMPLATED_PIECE:
return Object.assign({}, state, {contemplated_piece: action.payload.contemplated_piece})
default:
return state;
}
}
My combineReducers segment
import {combineReducers} from 'redux';
const allReducers = combineReducers({
Playlist: PlaylistReducer,
eventOptions: eventTicketReducer,
possibleMatches: PossibleMatchesReducer,
Intro: combineForms({
basicUserInfo: BasicUserInfoState,
GenderInfo: GenderInfoState,
ContactInfo: ContactInfoState
}, 'Intro'),
routing: routerReducer,
form: formReducer
});
Prop Values:
PossibleMatches.defaultProps = {
isFetching: true,
isFetched: false
}
My mapStateToProps function
function mapStateToProps(state){
return {UpperComponents: state.possibleMatches.UpperComponents,
LowerComponents: state.possibleMatches.LowerComponents,
contemplatedPiece: state.possibleMatches.contemplated_piece,
extraTops: state.possibleMatches.extraTops,
extraBottoms: state.possibleMatches.extraBottoms,
standaloneTops: state.possibleMatches.standaloneTops,
standaloneBottoms: state.possibleMatches.standaloneBottoms,
suggestedTops: state.possibleMatches.suggestedTops,
suggestedBottoms: state.possibleMatches.suggestedBottoms}
}
function mapDispatchToProps(dispatch){
return {
defaultPieces: () => {
dispatch(defaultPieces())
},
arrangePieces: () => {
dispatch(arrangePieces())
},
getCorrespondingPieces: () => {
dispatch(getCorrespondingPieces())
},
setEvaluatedPiece: () => {
dispatch(setEvaluatedPiece())
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PossibleMatches)
My Question is: What exactly is wrong with the way that I've implemented the promise. With the reducers and the redux actions setup correctly(I know because I've logged the fetched items to the console from the redux actions file), how can I properly populate the prop values in mapStateToProps. Currently the error is:
Im using React 16.4.0
A simple redux use case would seem as follows
possibleMatches.jsx (Component file)
class PossibleMatches extends React.Component {
state = {
isFetching: false
}
componentDidMount() {
this.setState({isFetching: true})
fetchingSomethingFromServer()
.then(resp => {
this.setState({isFetching: false})
this.props.UpdateRedux(resp)
});
}
render() {
const { isFetching } = this.state;
const { data } = this.props;
return (
isFetching ? <div>loading...</div> : <div>{data}</div>
)
}
}
export default connect(state => ({ data: state.possibleMatches.data }), {UpdateRedux})
actions.js (action creator file)
Use this action to update any data into redux
export const UpdateRedux = (data) => {type: 'UPDATE_REDUX', payload: data}
reducers.js
This is the file that holds the redux state
const defaultState = {
data: null
}
export default (state = defaultState, action) => {
switch(action.type) {
case 'UPDATE_REDUX':
return {data: action.payload};
default:
return state
}
}
In your combine reducers import this reducer and assign it as follows
import possibleMatches from 'reducers.js';
combineReducers({ possibleMatches });
Just a disclaimer - my code worked when I had problematic function in my main component. After I exported it it stopped behaving as it should. My theory is because somehow props are not being updated properly.
Anyway, I have a component, which after it's clicked it starts listening on window object and sets proper store element to "true" and depending on next object clicked acts accordingly. After incorrect object is clicked, the store should revert to false, and it does, however the props are still "true" as shown on the screenshot below.
How can I solve this? Perhaps there is a way that function could take store as parameter instead of props? Or im calling actions inproperly or im missing something completely?
Code below:
Main component (relevant parts?):
import React from 'react';
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import {activate} from '../../actions/inventory'
import { setModalContent, setModalState } from '../../actions/modal';
import inventoryReducer from '../../reducers/inventory';
import {chainMechanics} from './itemMechanics/chainMechanics';
class ItemRenderer extends React.Component{
handleBoltcuttersClicked(){
this.props.activate('boltcutters', true);
setTimeout(() => chainMechanics(this.props), 100)
}
inventoryItemRender(){
let inventoryItem = null;
if(this.props.inventory.items.boltcutters){
inventoryItem = <a className={this.props.inventory.activeItem.boltcutters ? "ghost-button items active " : "ghost-button items"} href="#" id='boltcuttersId' onClick={() => this.handleBoltcuttersClicked()}>Boltcutters</a>
}
return inventoryItem;
}
render(){
let renderItems = this.inventoryItemRender();
return(
<div>
{renderItems}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
level: state.level,
inventory: state.inventory
}
}
function mapDispatchToProps(dispatch) {
//dispatch w propsach
return(
bindActionCreators({activate: activate, setModalState: setModalState, setModalContent: setModalContent }, dispatch)
)
}
export default connect(mapStateToProps, mapDispatchToProps)(ItemRenderer);
File with problematic function:
import {activate} from '../../../actions/inventory'
import { setModalContent, setModalState } from '../../../actions/modal';
export function chainMechanics(props){
let clickedElement;
window.onclick = ((e)=>{
console.log(clickedElement, 'clickedelement', props.inventory.activeItem.boltcutters)
if(props.inventory.activeItem.boltcutters===true){
clickedElement = e.target;
if(clickedElement.id === 'chainChainedDoor'){
props.activate('boltcutters', false);
props.setModalContent('Chain_Broken');
props.setModalState(true);
} else if(clickedElement.id === 'boltcuttersId'){
console.log('foo')
} else {
props.activate('boltcutters', false);
props.setModalContent('Cant_Use');
props.setModalState(true);
console.log("props.inventory.activeItem.boltcutters", props.inventory.activeItem.boltcutters);
}
}
})
}
My actions:
const inventoryReducer = (state = inventoryDefaultState, action) => {
switch (action.type) {
case 'ACTIVE':
console.log(action)
return {
...state,
activeItem: {
...state.activeItem,
[action.item]: action.isActive
}
}
default:
return state;
}
}
How I configure store:
export default () => {
const store = createStore(
combineReducers({
level: levelReducer,
modal: modalReducer,
inventory: inventoryReducer,
styles: stylesReducer
}),
applyMiddleware(thunk)
)
return store;
}
I believe thats eveyrthing needed? If not please do let me know, I've been trying to make this work for a long time.
Screenshot:
You can use the React's function componentWillReceiveProps. That would trigger a rerender like this (and also make use of next props/state):
componentWillReceiveProps(next) {
console.log(next);
this.inventoryItemRender(next);
}
inventoryItemRender(next){
const inventory = next.inventory ? next.inventory : this.props.inventory;
let inventoryItem = null;
if(inventory.items.boltcutters){
inventoryItem = <a className={inventory.activeItem.boltcutters ? "ghost-button items active " : "ghost-button items"} href="#" id='boltcuttersId' onClick={(next) => this.handleBoltcuttersClicked(next)}>Boltcutters</a>
}
return inventoryItem;
}
handleBoltcuttersClicked(props){
this.props.activate('boltcutters', true);
setTimeout(() => chainMechanics(props), 100)
}