Connect does not call mapStateToProps under certain conditions - javascript

I am learning React, Redux and firebase; and creating a small word game.
There are three reducers (A, B and C).
The problem is that in the component shown below (simplified, but tested) mapStateToProps is only called when the states of B or C change; never with A.
There are several components and this component is the only one that experiences this problem. All the other components receive mapStateToProps call as expected.
I tested it by making an update to B/C whenever A is updated.
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Keyboard from './Keyboard';
const intiState = {
key: 0,
}
class PlayGame extends Component {
state=intiState;
componentDidMount() {
this.props.newGame();
}
render() {
return (
<div key={this.state.key}>
<div className="">
<Keyboard />
</div >
</div>
)
}
}
let mapDispatchToProps = {
newGame: () =>{return {type: 'NEW_GAME'}},
}
let mapStateToProps = (state) =>{
console.log("MapStateToProps ",state);
return{completeState: state};
}
export default connect(mapStateToProps, mapDispatchToProps)(PlayGame)

Related

Unable to update redux store - React Native

I am new to using redux for React Native and am testing it with a simple case. I have been able to successfully connect to the store, and I can see the action is dispatched properly using the redux debugger, however, the store is not updating in the debugger. I've tried several different implementations, but nothing is working. Any help would be appreciated!
Component:
import React, { PureComponent } from 'react'
import { Text, TouchableOpacity, SafeAreaView, Alert, Button } from 'react-native'
import { Navigation } from 'react-native-navigation';
import { connect } from 'react-redux'
import simpleAction from '../store/actions/simpleAction'
class App2 extends PureComponent {
constructor(props){
super(props);
}
pressRedux = () => {
const data = 'hello'
this.props.simpleAction(data)
}
render() {
return (
<SafeAreaView>
<Text>
{this.props.state.simpleReducer.text}
</Text>
<Button onPress = {this.pressRedux} title = 'Redux' />
</SafeAreaView>
)
}
}
function mapStateToProps(state) {
return {
state: state
};
}
const mapDispatchToProps = {
simpleAction
}
export default connect(mapStateToProps, mapDispatchToProps)(App2);
Action:
import {SET_TEXT} from '../types/types'
export default function simpleAction(data) {
return({
type: SET_TEXT,
payload: data
})
}
reducer:
import SET_TEXT from '../types/types'
const INITIAL_STATE = {
text: 'Hi'
}
const simpleReducer = (state = INITIAL_STATE, action ) => {
switch(action.type){
case SET_TEXT:
return { ...state, text: action.payload};
default:
return state;
}
}
export default simpleReducer;
The code you've shared here looks correct. Only thing I can suggest is, if you're seeing the action come through in the debugger, your issue is either with the data/payload or logic within simpleReducer.
In this case you have it properly stripped down so I'd almost think this isn't actually the code you are running, it might be something in your build process?

Why stream title is not rendering when whole stream object is returned from mapStateToProps?

I'm learning React from past two weeks.Currently I am taking course on Udemy of Stephen Grider.
I am building a dummy streaming platform just like twitch where I can create,delete,edit and watch streams.
Right now I was writing the StreamDelete component which will show a modal where the user can either delete the particular stream or cancel.
Here is the code what I have written:
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
import React from "react";
import Modal from "../modal";
import history from "../../history";
import { connect } from "react-redux";
import { fetchIndividualStream } from "../../actions";
class streamDelete extends React.Component {
componentDidMount() {
this.props.fetchIndividualStream(this.props.match.params.id);
}
action = () => {
return (
<React.Fragment>
<button className="ui button negative">Delete</button>
<button className="ui button">Cancel</button>
</React.Fragment>
);
};
renderContent = () => {
return `Are you sure you want to delete : ${
this.props.streams
? this.props.streams[this.props.match.params.id].title
: ""
}`;
};
render() {
return (
<Modal
title="Delete Stream"
content={this.renderContent()}
action={this.action()}
onDismiss={() => history.push("/")}
/>
);
}
}
const mapStateToProps = (state) => {
return { stream: state.streams };
};
export default connect(mapStateToProps, {
fetchIndividualStream,
})(streamDelete);
Here fetchIndividualStream action will fetch the stream which is to be deleted from the API and store it in the state.
The redux store after this operation looks like this
Output I'm getting:
What should be the OUPUT:
The modal will be present when the component first render with state as null as well as when the stream is successfully fetched.The only difference is after successful fetching the title will be displayed after "Are you sure you want to delete : ".See the renderContent method.
My problem is the modal is not displaying the title even after the state is updated just like the first image. I don't know the problem however on changing some code it worked.
The changes I made were in mapStateToProps and in the renderContent method.
mapStateToProps
const mapStateToProps = (state, ownProps) => {
return { stream: state.streams[ownProps.match.params.id] };
};
renderContent method:
renderContent = () => {
return `Are you sure you want to delete : ${
this.props.stream ? this.props.stream.title : ""
}`;
};
EDIT: Link to the Github repository
You're seems to be using react-router-dom and want to get parameter from the url?
You need to use withRoute HOC to get access to .match / this.props.match.params.id. Please refer to example below.
Docs: https://reacttraining.com/react-router/core/api/withRouter
connect(mapStateToProps, mapDispatchToProps)(withRouter(Dashboard))
import React from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
// A simple component that shows the pathname of the current location
class ShowTheLocation extends React.Component {
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
};
render() {
const { match, location, history } = this.props;
return <div>You are now at {location.pathname}</div>;
}
}
// Create a new component that is "connected" (to borrow redux
// terminology) to the router.
const ShowTheLocationWithRouter = withRouter(ShowTheLocation);
So in your particular case you need to do this.
export default connect(mapStateToProps, {
fetchIndividualStream,
})(withRouter(streamDelete)); // withRouter higher order component.

Importing React component with Redux methods

I put together a simple component to try and get my head around some React and Redux concepts.
I'm now trying to seperate the Redux logic into a seperate component, so that there's two components in total.
When I try to compile, all the methods/objects in NewComponent are undefined. When I add context with this I get the same errors.
Below is my original component (which works fine) before trying to put the Redux methods/objects in to a seperate component.
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { createStore, combineReducers } from 'redux'
import { Provider, connect } from 'react-redux'
const incrementAction = {
type: 'INCREMENT',
value: 1
}
const decrementAction = {
type: 'DECREMENT',
value: 1
}
function reducer(state = 0, action) {
if (action.type === 'INCREMENT') {
return state + 1
} else if (action.type === 'DECREMENT'){
return state - 1
} else {
return state
}
}
// Map Redux state to component props
function mapStateToProps(state) {
return {
value: state
}
}
// Map Redux actions to component props
function mapDispatchToProps(dispatch) {
return {
onIncrementClick: () => dispatch(incrementAction),
onDecrementClick: () => dispatch(decrementAction)
}
}
const store = createStore(reducer)
class View extends Component {
showState() {
console.log(store.getState())
}
render() {
const { value, onIncrementClick, onDecrementClick } = this.props
return (
<div className="App">
<header className="App-header">
<h1 className="App-title"></h1>
</header>
<h2>Counter</h2>
<div onClick = {onIncrementClick}> <p className="button"> + </p></div>
<div onClick = {onDecrementClick}> <p className="button"> - </p></div>
<div onClick = {this.showState}> <p className="button"> Show State </p></div>
</div>
);
}
}
View = connect(
mapStateToProps,
mapDispatchToProps
)(View)
const WrappedApp = () => (
<Provider store={store}>
<View />
</Provider>
);
export default WrappedApp;
EDIT
Here are the components after I tried to break up the above.
NewComponent:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { createStore, combineReducers } from 'redux'
import { Provider, connect } from 'react-redux'
class NewComponent extends Component {
constructor(props){
super(props)
const incrementAction = {
type: 'INCREMENT',
value: 1
}
const decrementAction = {
type: 'DECREMENT',
value: 1
}
const store = createStore(reducer)
}
reducer(state = 0, action) {
if (action.type === 'INCREMENT') {
return state + 1
} else if (action.type === 'DECREMENT'){
return state - 1
} else {
return state
}
}
// Map Redux state to component props
mapStateToProps(state) {
return {
value: state
}
}
// Map Redux actions to component props
mapDispatchToProps(dispatch) {
return {
onIncrementClick: () => dispatch(incrementAction),
onDecrementClick: () => dispatch(decrementAction)
}
}
}
export default NewComponent;
And View:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { createStore, combineReducers } from 'redux'
import { Provider, connect } from 'react-redux'
import NewComponent from './NewComponent'
class View extends Component {
showState() {
console.log(this.store.getState())
}
render() {
const { value, onIncrementClick, onDecrementClick } = this.props
return (
<div className="App">
<header className="App-header">
<h1 className="App-title"></h1>
</header>
<h2>Counter</h2>
<div onClick = {onIncrementClick}> <p className="button"> + </p></div>
<div onClick = {onDecrementClick}> <p className="button"> - </p></div>
<div onClick = {this.showState}> <p className="button"> Show State </p></div>
</div>
);
}
}
View = connect(
this.mapStateToProps,
this.mapDispatchToProps
)(View)
const WrappedApp = () => (
<Provider store={this.store}>
<View />
</Provider>
);
export default WrappedApp;
I'm now getting:
Could not find "store" in either the context or props of "Connect(View)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(View)".
I bet you have a technical background in OOP languages, right ? :)
A React Component is basically a function responsible of rendering something on the screen.
If you want to organize your Redux related code, you don't have to wrap it in a React Component. Simply declare the functions and objects you need in separate files and use JavaScript module import/export feature.
You can then follow the guidelines from Redux on how to organize your Redux code.
For example, for your Redux store, you can end up with a store.js file which contains something like
...
export const store = createStore(reducer);
...
And then use it where you want by simply importing it
import store from 'store.js'
You are not supposed to define the store twice as you do after splitting the code into two files. And you define a component that is not a real component. And these are only two things that are not great...
I don't think a good way to start building a react/redux project is by fixing the above code. A better way, that will serve you in the future, is the following:
First, download a basic redux code example. One that I recommend is ReduxSimpleStarter. It is a good example of how to organize your folder tree in a redux project.
Once you have reviewed the above example, create a new project using create-react-app, run it, then start modifying it based on the redux example.

react cannot re-render when state change in redux

I am starter in react & redux. I want to make todolist app. but when I insert a list into a store the displayList() function is not re-render. How can I fix this.
This is my reducer code.
export default function(state = ['start1','start2'], actions) {
switch(actions.type) {
case 'APPEND_ITEM':
state.push(actions.payload.item)
return state
break
}
return state
}
And this is my todolist.js code.
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import ReactDOM from 'react-dom'
import React from 'react'
class TodoList extends React.Component {
displayList(){
return this.props.dispatchs.map((item) => {
return(
<li key={ Math.random() }>{item}</li>
)
})
}
render(){
return(
<div>
{ this.displayList() }
</div>
)
}
}
var mapStateToProps = (state) => {
return {
dispatchs: state.dispatch
}
}
export default connect(mapStateToProps)(TodoList)
pin 2,3 does not re-render on the screen
Thank you for help.
Instead of using push in the reducer, return [ ... state, action.payload.item ]
case 'APPEND_ITEM':
return [ ...state, action.payload.item ]
This is because React will see the new state (object reference) equal to the old one, and decide not to re-render since it seems like nothing have changed

Trouble using actions in react-redux presentational component

I'm new to redux and having trouble wrapping my head around presentational and container components.
Relevant stack:
react v0.14.8
react-native v0.24.1
redux v3.5.2
react-redux v4.4.5
The issue:
I have a login button component, which when rendered checks the login status and calls the onSuccessfulLogin action which updates the state with the user's Facebook credentials.
However, when trying to separate this into separate presentational/container components, I'm unable to call the onSuccessfulLogin action: Error: onSuccessfulLogin is not defined.
What am I doing wrong here? I'd imagine there's something simple that I'm not understanding with the relationship between the two components and the connect() function.
Presentational Component (Login.js)
import React, { PropTypes } from "react-native";
import FBLogin from "react-native-facebook-login";
import UserActions from "../users/UserActions";
class LoginPage extends React.Component {
render() {
const { userData, onSuccessfulLogin } = this.props;
return (
<FBLogin
permissions={["email","user_friends"]}
onLoginFound= { data => {
onSuccessfulLogin(data.credentials);
}}
/>
)
}
};
export default LoginPage;
Container Component (LoginContainer.js)
import { connect } from 'react-redux';
import LoginPage from "../login/LoginPage";
import UserActions from "../users/UserActions";
const mapDispatchToProps = (dispatch) => {
return {
onSuccessfulLogin: (userData) => {
dispatch(UserActions.userLoggedIn(userData))
}
}
}
const mapStateToProps = (state) => {
return {
userData: state.userData
}
}
const LoginContainer = connect(
mapStateToProps,
mapDispatchToProps
)(LoginPage);
export default LoginContainer;
Also, if I wanted to make the updated state.userData accessible to the LoginPage component, how would I do that? Any help is appreciated!
Solved! When using ES6 classes, you're required to call super(props) in a constructor method in order to access the container's properties in the connected presentational component:
class LoginPage extends React.Component {
constructor(props){
super(props);
}
render(){
// ...
}
}
Your container component is supposed to be a component and it must have a render function with the dumb/presentational components you want to render.
import { connect } from 'react-redux';
import LoginPage from "../login/LoginPage";
import UserActions from "../users/UserActions";
class LoginContainer extends React.Component {
constructor(props){
super(props);
}
render() {
return (
<LoginPage userData={this.props.userData}
onSuccessfulLogin={this.props.onSuccessfulLogin}
/>
)
}
};
const mapDispatchToProps = (dispatch) => {
return {
onSuccessfulLogin: (userData) => {
dispatch(UserActions.userLoggedIn(userData))
}
}
}
const mapStateToProps = (state) => {
return {
userData: state.userData
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(LoginPage);

Categories

Resources