简体   繁体   中英

react router redux is not rendered on props changing

I'm struggling on using react router redux and everything is fine except rendering on props changing.

Index.js

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {createStore, combineReducers, applyMiddleware} from 'redux';
import {Router, Route, browserHistory} from 'react-router';
import { syncHistoryWithStore, routerReducer, routerMiddleware } from 'react-router-redux';
import App from './app';
import Sign from '../sign/app';
import SignInfo from '../sign/redux/reducer';

let store = createStore(
  combineReducers({
    SignInfo,
    routing: routerReducer
  }),
);

const history = syncHistoryWithStore(browserHistory, store);

class Index extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    console.log('root')
    return (
      <Provider store={store}>
        <Router history={history}>
          <Route path="/" component={App}/>
          <Route path="/sign" component={Sign}/>
        </Router>
      </Provider>
    );
  }
}

ReactDOM.render(<Index/>, document.getElementById('root'));

sign.js

import React from 'react';
import {connect} from 'react-redux';
import Belle from 'belle';
import {Grid, Row, Col} from 'react-flexbox-grid';
import { withRouter } from 'react-router';
import SignPaze1 from './signPaze1'

class App extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    const mainCardStyle = {
      border: '1px solid #aaa',
    }
    return (

      <div className="sign-page">
        <SignPaze1/>
      </div>
    )
  }
}

function mapStateToProps(state) {
  return {
  }
}

export default withRouter(connect(mapStateToProps)(App));

signPaze1.js

import React from 'react';
import { bindActionCreators } from 'redux';
import {connect} from 'react-redux';
import Belle from 'belle';
import { withRouter } from 'react-router';
import {Grid, Row, Col} from 'react-flexbox-grid';

import {handleSignInput} from './redux/action';

class SignPaze1 extends React.Component {
  constructor(props) {
    super(props);

    this.handleInputValue = this.handleInputValue.bind(this);

  }

  handleInputValue(e) {
    let {dispatch} = this.props;
    console.log('on render')
    const action = bindActionCreators(handleSignInput, dispatch)
    action(e.target.id, e.target.value);
  }
  render() {
    let signData = this.props.signData;
    let mainCardStyle = {
      border: '1px solid #aaa',
    }
    return (
      <Grid>
        <Row>
          <Col xs={12}>
            <Belle.Card style={mainCardStyle}>
              <Row center="md">
                <Col xs={12} sm={6} md={4} lg={3}
                     className="input-group">
                  <input type="text"
                         id="company_number"
                         value={signData.company_number}
                         onChange={this.handleInputValue}/>
                </Col>
                <Col xs={12} sm={6} md={4} lg={3}
                     mdOffset={1} lgOffset={2}
                     className="input-group">
                  <Belle.TextInput
                    onUpdate={this.handleInputValue.bind(this, 'data')}/>
                </Col>
              </Row>
              <Row center="md">
                <Col xs={12} sm={6} md={4} lg={3}
                     className="input-group">
                  <Belle.TextInput
                    onUpdate={this.handleInputValue.bind(this, 'da2')}/>
                </Col>
                <Col xs={12} sm={6} md={4} lg={3}
                     mdOffset={1} lgOffset={2}
                     className="input-group">
                  <Belle.TextInput
                    onUpdate={this.handleInputValue.bind(this, 'dat3')}/>
                </Col>
              </Row>
            </Belle.Card>
          </Col>
        </Row>
      </Grid>
    )
  }
}

function mapStateToProps(state) {
  console.log(state.SignInfo);
  return {
    signData: state.SignInfo.signData,
  }
}

export default withRouter(connect(mapStateToProps)(SignPaze1));

reducer.js

import {HANDLE_SIGN_INPUT} from './action';

const initialMainState = {
  signData: {
    company_number: 'asdas|',
    company_name: '',
    username: '',
    email: '',
    email_confirm: '',
    password: '',
    password_confirm: '',
  }
}

function SignInfo(state = initialMainState, action) {
  let newVal = {};
  switch (action.type) {
    case HANDLE_SIGN_INPUT:
      newVal = Object.assign({}, state);
      newVal.signData[action.target] = action.value;
      return newVal;
    default:
      return state;
  }
}

export default SignInfo;

someone says that add withRouter function on export line will fix this error, but it doesn't. ( here ). However is normally render again when state which is member of SignPaze1 is changed.

How can I fix this problem causd by react-router-redux?

edit

action.js

export const HANDLE_SIGN_INPUT = 'HANDLE_SIGN_INPUT';

export function handleSignInput(target, value){
  return {
    type: HANDLE_SIGN_INPUT,
    target: target,
    value: value,
  }
}

I believe the issue here is that you should be using combineReducers , instead of a single reducer containing nested objects.

You probably need to create a reducer specifically for signData , and use Redux's combineReducers method.

New file: signDataReducer.js

import {HANDLE_SIGN_INPUT} from './action';

const initialMainState = {
  company_number: 'asdas|',
  company_name: '',
  username: '',
  email: '',
  email_confirm: '',
  password: '',
  password_confirm: '',
};

export default function SignData (state = initialMainState, action) {
  let newVal = {};
  switch (action.type) {
    case HANDLE_SIGN_INPUT:
      newVal = Object.assign({}, state);
      newVal[action.target] = action.value;
      return newVal;
    default:
      return state;
  }
}

reducer.js

import { combineReducers } from 'redux';
import signDataReducer from './signDataReducer';

export default combineReducers({
  signDataReducer
});

The reason it wasn't working before is because of how Object.assign works, it copies the reference of signData to the new object created by assign.

Then when the SignPaze1 props are compared by shallow comparison, signData is the exact same object as before, so is considered equal. And no render occurs.

ie

const initialMainState = {
  signData: {
    company_number: 'asdas|',
    company_name: '',
    username: '',
    email: '',
    email_confirm: '',
    password: '',
    password_confirm: '',
  }
}
const newState = Object.assign({}, initialMainState);


newState.signData.company_name = 'Hello!';
// => "Hello!"
initialMainState.signData.company_name
// => "Hello!"
newState.signData === initialMainState.signData
// => true

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM