Родительский компонент отправляет данные, но потомки не перерисовывают, только когда нажимают клавишу на входе.
SMART Я отправляю состояние формы userValues, если я помещаю console.log (this.props.state.userValues) в render (), компонент smart render, когда при редуксе получают новые свойства.
import React, { Component, RefObject } from 'react';
import { bindActionCreators } from 'redux';
import CSSModules from 'react-css-modules';
import { connect } from 'react-redux';
import WebappHeader from '../../containers/WebappHeader/WebappHeader';
import BlueSection from '../../containers/BlueSection/BlueSection';
import Title from '../../components/Title/Title';
import { getBase64, isString, userData } from '../../helpers';
import * as styles from './MyAccount.css';
import * as actions from './../../actions/accounts';
import { updateAccountInformation, getAccountInformation } from '../../services/AccountService';
import UserProfile from '../../components/UserProfile/UserProfile';
interface State {
tabActiveClass: string;
extensions: string;
file_64: string;
unsavedChanges: boolean;
}
interface Props { state: any, actions: any }
class MyAccount extends Component <Props, State> {
private fileInput: RefObject <HTMLInputElement>;
private avatarImage: RefObject <HTMLImageElement>;
constructor(props: any) {
super(props);
this.state = { ...this.props.state }
this.avatarImage = React.createRef();
this.fileInput = React.createRef();
}
componentDidMount() {
setTimeout(() => {
getAccountInformation().then(result => {
result.user
? this.props.actions.userAccountLoad(result.user)
: null
});
}, 1000)
// setea la primera tab como la que esta activa
this.setState({ tabActiveClass: document.getElementsByClassName('tablinks')[0].classList[2] });
}
handleSubmit = (userData: any): void => {
// console.log(userData)
updateAccountInformation(userData)
.then((result: any) => {
!result.error
? this.props.actions.userAccountUpdate(result)
: this.props.actions.userAccountError()
})
}
private uploadAvatar(files: any): void {
if (files.length > 0){
let file = files[0], extensions_allowed = this.state.extensions.split(',');
let extension = `.${file.name.split('.').pop().toLowerCase()}`;
if(extensions_allowed.indexOf(extension) === -1){
alert(`This extension is not allowed. Use: ${this.state.extensions}`);
this.fileInput.current!.value = '';
} else {
getBase64(file, (result: any) => {
this.setState({file_64: result, unsavedChanges: true});
this.avatarImage.current!.src = result;
console.log(result); // nueva img, ejecutar disparador
});
}
}
}
private changeTab(e: any, name: string): void {
let i: number;
const future_tab: any = document.getElementById(name);
const tabcontent: any = document.getElementsByClassName('tabcontent');
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = 'none';
}
const tablinks: any = document.getElementsByClassName('tablinks');
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(` ${this.state.tabActiveClass}`, '');
}
future_tab.style.display = 'flex';
e.currentTarget.classList.add(this.state.tabActiveClass);
}
public render() {
return (
<BlueSection styleName="section">
<WebappHeader />
<div styleName="tabs-headers">
<div className="wrapper">
<Title text="Account Information" />
<ul styleName="list">
<li styleName="item active" className="tablinks" onClick={(e: any) => this.changeTab(e, 'profile')}>
Profile
</li>
<li styleName="item" className="tablinks" onClick={(e: any) => this.changeTab(e, 'activity')}>
Activity
</li>
<li styleName="item" className="tablinks" onClick={(e: any) => this.changeTab(e, 'plan')}>
Plan & Billing
</li>
</ul>
</div>
</div>
<div styleName="tabs-body">
<div className="wrapper">
<ul styleName="list">
<li styleName="item" id="profile" className="tabcontent" style={{'display':'flex'}}>
<UserProfile
onSubmit={this.handleSubmit}
userValues={this.props.state.values}
// avatarImage={this.avatarImage}
// uploadAvatar={this.uploadAvatar}
// fileInput={this.fileInput}
/>
</li>
<li styleName="item" id="activity" className="tabcontent">
</li>
<li styleName="item" id="plan" className="tabcontent">
</li>
</ul>
</div>
</div>
</BlueSection>
)
}
}
const mapStateToProps = (state: any) => ({ state: state.account });
const mapDispatchToProps = (dispatch: any) => ({ actions: bindActionCreators(actions, dispatch) });
const ComponentWithCSS = CSSModules(MyAccount, styles, { allowMultiple: true });
export default connect(mapStateToProps, mapDispatchToProps)(ComponentWithCSS);
CHILDREN
Я получаю userValues из смарт-компонента формы, если я помещаю console.log (это.props.userValues) в render (), компонент не рендерится при получении новых свойств.
import React, { Component, Fragment } from 'react';
import CSSModules from 'react-css-modules';
import { InjectedFormProps, reduxForm, Field } from 'redux-form';
import { connect } from 'react-redux';
import Heading from '../Heading/Heading';
import Button from '../Button/Button';
import Input from '../Input/Input';
import * as styles from './UserProfile.css';
interface Props {
userValues: any,
avatarImage: any,
uploadAvatar: any,
fileInput: any,
handleSubmit: any,
}
const inputField = ({ input, label, type, meta, disabled, field_value }: any) => (
<div>
<Input
{...input}
labelText={label}
styleName="input"
type={type ? type : "text"}
disabled={disabled ? disabled : false}
placeholder={field_value ? field_value : ''}
/>
{/* fix mejorar mensajes css */}
{
meta.error && meta.touched && <span>{meta.error}</span>
}
</div>
)
const isRequired = (value: any) => (
value ? undefined : 'Field Required'
)
class UserProfile extends Component<Props & InjectedFormProps<{}, Props>> {
constructor(props: any) {
super(props);
}
componentWillMount() { this.props.initialize({ name: this.props.userValues.name }) }
public render() {
console.log(this.props.userValues)
// setTimeout(() => {
// this.forceUpdate() // temp (bad) fix
// }, 2000)
return (
<Fragment>
<div styleName="right-info">
<Heading text="Your data" styleName="heading" />
<form id="UserProfile" onSubmit={this.props.handleSubmit} method="POST" styleName="form">
<fieldset styleName="fieldset">
<Field
name="name"
label="Name"
validate={isRequired}
placeholder={this.props.userValues.name}
component={inputField} />
<br />
<Input
labelText="Email"
styleName="input"
type="email"
disabled={true}
placeholder={this.props.userValues.email} />
</fieldset>
<fieldset styleName="fieldset">
<Field
name="phone_number"
label="Phone Number"
validate={isRequired}
component={inputField} />
<br />
<Field
name="company"
label="Company"
validate={isRequired}
component={inputField} />
</fieldset>
</form>
<Heading text="Notifications" styleName="heading" />
<form styleName="notification-form">
<label styleName="label">
I wish to recieve newsletters, promotions and news from BMS
<input type="checkbox" name="notifications" />
</label>
<p styleName="disclaimer">
<span styleName="bold">Basic information on Data Protection:</span> BMS collects your data too improve our services and, if given consent, will keep you updated on news and promotions of BMS projects. +Info Privacy Policy
</p>
</form>
</div>
<div styleName="cta-wrapper">
<Button
onClick={this.props.handleSubmit}
text="SAVE CHANGES"
filled={true}
// disabled={!this.props.state.unsavedChanges}
/>
</div>
</Fragment>
)
}
}
const UserProfileWithCSS = CSSModules(UserProfile, styles, { allowMultiple: true });
export default connect()(reduxForm<{}, Props>({ form: 'UserProfile' })(UserProfileWithCSS));
REDUCER Я думаю, что все в порядке
import { USER_ACCOUNT_UPDATE, USER_ACCOUNT_LOAD, USER_ACCOUNT_ERROR } from './../actions/types';
import { userData } from '../helpers';
const engine = require('store/src//store-engine');
const storages = require('store/storages/sessionStorage');
const store = engine.createStore(storages);
const INITIAL_STATE = {
tabActiveClass: '',
extensions: '.jpeg,.jpg,.gif,.png',
file_64: '',
unsavedChanges: false,
values: {
name: '',
email: '',
phone_number: '',
company: '',
notifications: false
},
};
export default function (state = INITIAL_STATE, action: any) {
switch (action.type) {
case USER_ACCOUNT_LOAD: {
let newState = state;
newState.values.name = action.payload.profile.first_name;
newState.values.email = action.payload.email;
newState.values.phone_number = action.payload.profile.phone_number;
newState.values.company = action.payload.profile.company;
return { ...newState };
}
case USER_ACCOUNT_UPDATE: {
let newState = state;
let storage = store.get('user_data');
storage.profile.first_name = action.payload.data.name;
storage.profile.company = action.payload.data.company;
storage.profile.phone_number = action.payload.data.phone_number;
newState.values.name = action.payload.data.name;
newState.values.phone_number = action.payload.data.phone_number;
newState.values.company = action.payload.data.company;
store.set('user_data', storage);
return { ...newState };
}
case USER_ACCOUNT_ERROR:
return { ...state };
default:
return state;
}
}