I'm trying to dispatch actionC from TestClass so that Labelclass can receive the state change from reducer as below.
TestClass
class Test extends React.Component {
constructor(props){
super(props)
this.state = {text:props.text,onClick:props.onClick}
this.onInput = this.onInput.bind(this)
this.onSubmit = this.onSubmit.bind(this)
}
onInput(e){
this.setState({text:e.target.value})
}
onSubmit(e){
this.state.onClick(this.state.text)
}
render(){
return <div>
<form onSubmit={this.onSubmit}>
<input value={this.state.text} onChange={this.onInput} />
<button type="submit">Add Todo</button>
</form>
</div>
}
}
function mapDispatchToProps_Test(dispatch,ownProps){
return {onClick:(id)=>dispatch(actionC(id))}
}
Test.propTypes = {
text:PropTypes.string.isRequired,
onClick:PropTypes.func.isRequired
}
Test = connect(null,mapDispatchToProps_Test)(Test)
LabelClass and Entry
class Label extends React.Component {
constructor(props){
super(props)
this.state = {text:props.text}
}
render(){
return <div> Hello<label>{this.props.text}</label></div>
}
}
function mapStateToProps_Label(state,ownProps){
return {
text:state.text
}
}
Label = connect(mapStateToProps_Label)(Label)
Label.propTypes = {
text:PropTypes.string.isRequired
}
const App = () =>(
<div>
<Test text="" onSubmit onClick />
<Label text=""/>
</div>
)
ReactDOM.render(
<Provider store={store}><App /></Provider>,
document.getElementById('root')
);
Action and Reducer
const CDD_TODO = 'CDD_TODO'
const {PropTypes} = React;
const {createStore} = Redux;
const { Provider, connect } = ReactRedux;
let store = createStore(reducer)
//action
function actionC(text) {
console.log(CDD_TODO)
return { type: CDD_TODO, text:text }
}
function reducer(state = {}, action) {
switch (action.type) {
case CDD_TODO:
console.log("action",action.text)
return Object.assign({}, state, {
text:action.text
})
default:
return state
}
}
The trouble is the output from LabelClass render() becomes invisible at once
after displayed in a moment.
I want it not to disappear. What is the cause?
You didn't map the value text from your reducer you created but you mapped the reducer it self. In your case, you have to map the text value from the reducer named text:
function mapStateToProps_Label(state,ownProps){
// state.text is the state of your reducer
// state.text.text is one of the state value
return {
text:state.text.text
}
}
Besides, from what I see, you needn't a state in Label:
class Label extends React.Component {
render(){
return <div> Hello<label>{this.props.text}</label></div>
}
}
Same thing in Test: for onClick on this.state is useless:
class Test extends React.Component {
constructor(props) {
super(props);
this.state = { text: props.text }
this.onInput = this.onInput.bind(this)
this.onSubmit = this.onSubmit.bind(this)
}
onInput(e) {
this.setState({ text: e.target.value });
}
onSubmit(e) {
this.props.onClick(this.state.text);
}
render() {
return <div>
<form onSubmit={this.onSubmit}>
<input value={this.state.text} onChange={this.onInput} />
<button type="submit">Add Todo</button>
</form>
</div>;
}
}
I think you should put a breakpoint in mapStateToProps to see if text si modified after having been set. You should put a break point in the reducer to see if an action dispatch an action that erase the text data.
Related
When I try to submit the search form, it doesn't call the search action.
Is it because I'm passing the search function inefficiently as I'm passing it from one component to another? is there a way I could just use it directly from the import?
If I were to include the SearchBox component inside of Nav itself, it works.
If I added console.log(search) inside of handleSubmit, it outputs the function.
import { search } from './actions/userActions';
class Nav extends React.Component {
render() {
const { search, self } = this.props;
return (
self === null
? <NavLoggedOut search={search} />
: <NavLoggedIn search={search}
)
}
}
const NavLoggedOut = (props) => {
.....
}
const NavLoggedIn = (props) => {
return (
<header>
<SearchBox search={search} />
</header>
)
}
class SearchBox extends React.Component {
constructor() {
super();
this.state = {
name: ''
}
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
handleSubmit = event => {
event.preventDefault();
this.props.search(JSON.stringify({name: this.state.name}))
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" id="name" onChange={this.handleChange} placeholder="Search" />
<button type="submit">search</button>
</form>
)
}
}
const mapStateToProps = state => ({
self: state.store.self
});
export default connect(mapStateToProps, {search})(Nav);
Try adding these few lines
import { bindActionCreators } from 'redux';
...
function mapDispatchToProps(dispatch){
return bindActionCreators({
search
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Nav);
Now your search action should be available to the component Nav using this.props.search()
Remember to pass in parameters if it is expecting one (This depends on how you've written your search action)
Add this in your constructor
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
and constructor should look like dis
constructor(props) {
super(props);
this.state = {
name: ''
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
I am trying to make an edit page. I am update the state for any changes made. I want to compare the initial state with the last state on the last save. but I can not control the first state.
export default class extends Component {
constructor(props){
super(props);
this.changeDetails = this.changeDetails.bind(this);
this.state = {
driver: this.props.driver
}
}
changeDetails = value => {
this.setState({
driver:value
})
}
onRegister = () => {
//I want to make a comparison here.
}
render() {
const {driver} = this.state
return (
<div>
<EditView driver={driver} changeDetails={this.changeDetails}/>
</div>
);
}
}
EditView.js
export default class extends Component {
render() {
const { driver} = this.props;
const changeDetails = event => {
driver['fname] = event.target.value;
this.props.changeDetails(driver);
};
return (
<div>
<Input
value={driver.fname}
onChange={event => changeDetails(event)}
/>
</div>
);
}
}
Do not mutate driver itself directly. Use something like this:
const changeDetails = event =>
this.props.changeDetails( { ...driver, fname: event.target.value } );
I have 2 Components one called NodeWidget and another called PopupWidget. In the NodeWidget it has a Model assigned to it which looks like the following:
PopupModel
export class PopupModel {
question: string;
model: string;
constructor(question: string, model: string) {
this.question = question;
this.model = model;
}
}
The parent Component is NodeWidget which passes in the Model to the PopupWidget with data in.
NodeWidget
{ this.state.showComponent ?
<PopupWidget model={this.props.popupModel} /> :
null
}
Then finally in the child Component we have this code:
export interface PopupWidgetProps {
model: PopupModel;
}
export interface PopupWidgetState { }
export class PopupWidget extends React.Component<PopupWidgetProps, PopupWidgetState> {
constructor(props: PopupWidgetProps) {
super(props);
this.state = { };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this.props);
}
render() {
return (
<div className="popup">
<div className="popup_inner">
<h1>TEST</h1>
<input type="text" value={this.props.model.question} placeholder="Write a question..." />
<button onClick={this.handleClick}>close me</button>
</div>
</div>
);
}
}
I want to be able to bind the value of the input to the model and then for it to update the original model in the Parent Component, am i doing this correctly as it does not seem to work.
You can do this to pass the input result to parent component on the button click:
PopupWidget :
export class PopupWidget extends React.Component<PopupWidgetProps, PopupWidgetState> {
constructor(props: PopupWidgetProps) {
super(props);
this.state = { question: '' };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.inputResult(this.state.question)
}
render() {
return (
<div className="popup">
<div className="popup_inner">
<h1>TEST</h1>
<input type="text" value={this.state.question} onChange={(question) => { this.setState({ question })}} placeholder="Write a question..." />
<button onClick={this.handleClick}>close me</button>
</div>
</div>
);
}
}
NodeWidget :
constructor(props) {
super(props);
this.getInputResult = this.getInputResult.bind(this);
}
getInputResult(question) {
this.props.inputResult(question);
this.setState({ showComponent: false });
}
...
{ this.state.showComponent ?
<PopupWidget inputResult={this.getInputResult} /> :
null
}
Finally in PopupModel (i assume this is a react component, i don't know if you can work with simple es6 class in react):
export class PopupModel extends React.Component {
constructor(props) {
this.state = { question: '', model: '' }; // set your initial state
this.getInputResult = this.getInputResult.bind(this);
}
getInputResult(question) {
this.setState({ question }); // here's our result from the input
}
render(){
return(<NodeWidget inputResult={this.getInputResult} />);
}
}
This can be pretty boring to handle if you have multiple components between the two which have to communicate.
You can use a HOC like Redux or MobX to handle an app state that can be passed in any component, and any component can dispatch actions to update the app state, you should go for it if you have multiple cases like this.
I have a class of this form:
export default class FixedMem {
constructor(totalMem){
this._totalMem = totalMem
}
get totalMem(){
return this._totalMem
}
addMem(mem){
this._totalMem += mem
}
}
I import it into my react component like this :
import Fixed from '../somewhere'
If i want to create a new classes with varying parameters based on input from a textbox and display its values. How do i call its methods from inside the render method ?. This somewhat illustrates my problem
class fixedBlock extends Component {
constructor(){
super()
this.state = {
"textInput":"",
"totalMem":0,
"fixed":null
}
}
handleInputChanged(e){
this.setState({
"textInput":e.target.value
})
}
handleButtonPressed(){
this.setState({"fixed":new Fixed(parseInt(this.state.textInput))})
}
incrementButtonPressed(){
this.state.fixed.addMem(2)
}
render(){
return(
<div>
<input type="button" onClick={this.handleInputChanged} value=
{this.state.textInput}>
<button onClick={this.handleButtonPressed}>create</button>
<button onClick={this.incrementButtonPressed}> increment </button>
<p>{this.state.fixed.totalMem}</p>
</div>
)
}
}
this doesn't work, another approach i had to solve this problem was using closures, so inside my react component :
class fixedBlock extends Component{
constructor(){//stuff here}
FixedMem () {
var FixedObj = null
return {
initFixed: function (totalMem) {
FixedObj = new Fixed(totalMem, divisions)
},
totalMem: function () {
return FixedObj.totalMem
},
increment: function(){
FixedObj.addMem(2)
}
render(){//stuff here}
}
How do i even use this in the render method ?
There are several issues with your code example. Missing closing tags and rebinding of methods missing.
Here's an example of dynamically usage of a class instance in a React component. However I can not recommend to use this approach. This is mainly as proof of concept.
class MyValue {
constructor(val) {
this._val = parseInt(val, 10) || 0;
}
get total() {
return this._val;
}
set total(val) {
this.val = val;
}
add(val) {
this._val += val;
}
subtract(val) {
this._val -= val;
}
}
Here's the React component
class Block extends React.Component {
constructor() {
super();
this.state = {
textInput: "",
myValue: new MyValue()
};
}
handleInputChanged(e) {
this.setState({
textInput: e.target.value
});
}
handleButtonPressed() {
this.setState({ myValue: new MyValue(this.state.textInput) });
}
incrementButtonPressed() {
this.state.myValue.add(2);
this.forceUpdate(); /* React does not know the state has updated, force update */
}
render() {
return (
<div>
<input type="number" step="1" onChange={this.handleInputChanged.bind(this)} />
<button onClick={this.handleButtonPressed.bind(this)}>create</button>
<button onClick={this.incrementButtonPressed.bind(this)}>increment</button>
<p>{this.state.myValue.total}</p>
</div>
);
}
}
As an alternative approach. You could use a pattern where you separate logic from presentation. Here's an example using function as child. The Calculator handles the calculation and Presentation uses the calculator and present the GUI.
class Calculator extends React.Component {
constructor() {
super();
this.state = {value: 0};
}
add(value){
this.setState(prevState => ({value: prevState.value + value}));
}
subtract(value){
this.setState(prevState => ({value: prevState.value - value}));
}
set(){
this.setState(prevState => ({value: parseInt(prevState.input, 10) || 0}));
}
input(value){
this.setState({input: value});
}
render() {
return this.props.children(
{
value: this.state.value,
add: this.add.bind(this),
subtract: this.subtract.bind(this),
set: this.set.bind(this),
input: this.input.bind(this),
});
}
}
const Presentation = props => (
<Calculator>
{ ({value,add,subtract,set,input}) => (
<div>
<button onClick={() => add(2)}>add 2</button>
<button onClick={() => subtract(3)}>subtract 3</button>
<input type="number" step="1" onChange={e => input(e.target.value)} />
<button onClick={set}>set</button>
<p>{value}</p>
</div>)
}
</Calculator>);
The problem with the first attempt is that you are mutating a Component's state without letting React know about it. You need to use setState() or forceUpdate(). One way to still have FixedMem manage your state while letting React know could be:
class FixedBlock extends Component {
constructor(props) {
super(props);
this.state = {
textInput: '',
totalMem: 0
};
this.fixedMem = new FixedMem(0);
this.sync = this.sync.bind(this);
}
sync() {
const totalMem = this.fixedMem.totalMem;
this.setState({ totalMem });
}
handleInputChanged(evt) {
this.setState({ textInput: evt.target.value });
}
handleButtonPressed() {
this.fixedMem = new FixedMem(parseInt(this.state.textInput));
this.sync();
}
incrementButtonPressed() {
this.fixedMem.addMem(2);
this.sync();
}
render() {
return (
<div>
<input type="text" onChange={this.handleInputChanged.bind(this)} />
<button onClick={this.handleButtonPressed.bind(this)}>create</button>
<button onClick={this.incrementButtonPressed.bind(this)}>increment</button>
<p>{this.state.totalMem}</p>
</div>
);
}
}
I am very new to react and redux. I have created an application using react/redux. I have implemented routing in the application and also have able to manage the state using redux. Now my problem is After set the state i am only able to get the props in render() not anywhere in the component. below are my code,
export class EmpSearch extends React.Component {
constructor(props) {
super(props);
this.state = {
Empnumber: ''
};
}
EmpSearch = (e) => {
if (e.key === 'Enter') {
browserHistory.push('/Emp/' + e.target.value);
}
}
updateEmpNumber(e) {
this.props.dispatch({
type: 'UPDATE_EMP_NUMBER',
payload: e.target.value
});
}
render() {
return (
<div className="row">
<form>
<div className="form-group">
<label htmlFor="Empnumber">Emp Number</label>
<input type="text" className="form-control" id="Empnumber" placeholder="Emp Number" value={this.props.Empnumber} onChange={this.updateEmpNumber.bind(this)} onKeyPress={this.EmpSearch}/>
</div>
</form>
</div>
);
}
}
function mapStateToProps(state){
return {
Empnumber: state.Empnumber
}
}
export default connect(mapStateToProps)(EmpSearch);
I want to pass the value to below component but instead of passing and getting value to render i want to create a method and want to pass the props there only.
class EmpDetail extends React.Component {
render() {
const empNumber = this.props.Empnumber;
return (
<div className="container">
Empnumber = {empNumber}
</div>
);
}
}
function mapStateToProps(state){
return {
Empnumber: state.Empnumber
}
}
export default connect(mapStateToProps)(EmpDetail);
I am successfully able to get the props value in render but could not in function.I have tried a lot to to create a method and pass the props to the method and get the props value but could not. Please help me out.
The problem is that you are not binding your function and hence in the function this refers to the context of the function. Also componentDidMount or componentWillMount will be executed only once and hence will contain only the inintial value
Second function
class EmpDetail extends React.Component {
constructor(props) {
super(props);
this.handleProp=this.handleProp.bind(this);
}
componentWillReceiveProps(nextProps) {
this.handleProp(nextProps);
}
handleProp(props) {
console.log('In function' + props.Empnumber);
return props.Empnumber;
}
render() {
const empNumber = this.props.Empnumber;
return (
<div className="container">
Empnumber = {this.props.Empnumber}
</div>
);
}
}
function mapStateToProps(state){
return {
Empnumber: state.Empnumber
}
}
export default connect(mapStateToProps)(EmpDetail);