I have 2 TextField in 2 components.
When a TextField change value, how I can send and change value of TextField of remaining?
This is example for my issue. This is my issue.
I have url http://localhost:8000/search?search=php&category=catqgkv4q01ck7453ualdn3sd&page=1
Search page Js:
class SearchPage extends Component {
constructor(props) {
super(props);
let search = typeof this.props.location.query.search !== '' ? this.props.location.query.search : '';
if(search){
this.props.dispatch(setTextSearch(search));
}
};
render() {
return (
<MuiThemeProvider>
<div id='search-page'>
<SearchTextBox textSearch={this.props.textSearch}/>
</div>
</MuiThemeProvider>
)
}
}
// Retrieve data from store as props
function mapStateToProps(state) {
return {
textSearch: getTextSearch(state)
}
}
SearchPage.contextTypes = {
router: React.PropTypes.object
};
export default connect(mapStateToProps)(SearchPage);
Search Action:
import callApi from '../../util/apiCaller';
// Export Constants
export const ACTIONS = {
SET_TEXT_SEARCH: 'SET_TEXT_SEARCH'
};
export function setTextSearch(search) {
return {
type: ACTIONS.SET_TEXT_SEARCH,
search
};
}
Search Reducer:
import { ACTIONS } from './SeachActions';
// Initial State
const initialState = {
textSearch: '',
};
const SearchReducer = (state = initialState, action) => {
switch (action.type) {
case ACTIONS.SET_TEXT_SEARCH:
state.textSearch = action.search;
return {...state};
default:
return state;
}
};
/* Selectors */
export const getTextSearch = state => state.categories.textSearch;
// Export Reducer
export default SearchReducer;
I have component SearchTextBox
import React from 'react';
import TextField from 'material-ui/TextField';
export default class SearchTextBox extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.textSearch,
};
};
render() {
return (
<TextField
hintText="Search"
className="search-txtbox"
ref='searchText'
style={{height : '40'}}
underlineShow={false}
value={this.state.value}
onChange={this.handleChange}
autoFocus
onKeyPress={this.handleEnter}
/>
);
}
}
How can I change value by data parameter "search" on URL
So your problem seems to be about sharing the same data with other components ( passing some kind of data to each other, not only the react component state, could be anything ).
You should be aware of the available ways to communicate data between components.
1 - Props
2 - Context
3 - Global variables ( anti - pattern until you really need, when you need you should use redux or similar that makes use of both props and context to create a big global data tree without creating global variables )
So there is no other way to communicate data between components.
Then since we know available options, second question becomes how are the component I want to communicate data between positioned relative to each other.
1 - One is the direct parent of another.
2 - One is the indirect parent of another.
3 - Both share the same parent.
Assuming your TextFields share the same parent, luckily, here is a working code for you to get the idea.
const TextField = ({
value = '', handleInputChange = ''
}) => <input type="text" value={value} onChange={ handleInputChange }/>
class ParentC extends React.Component {
state = {
sharedInputValue : ''
}
constructor(props){
super(props)
this.state = {
sharedInputValue : ''
}
this.handleInputChange = this.handleInputChange.bind(this)
}
handleInputChange( event ){
this.setState({ sharedInputValue : event.target.value})
}
render(){
return (
<div>
<TextField
value={ this.state.sharedInputValue }
handleInputChange={ this.handleInputChange }
/>
<TextField
value={ this.state.sharedInputValue }
handleInputChange={ this.handleInputChange }
/>
</div>
)
}
}
Related
I've created a form in react and after some research i think that if you don't want to use an external library to manage the form, the context could be the best choice, expecially in my case where i've many nested component that compose it.
But, i'm not sure that putting a function inside my state is a good thing.
But let me give you some code:
configuration-context.js
import React from 'react'
export const ConfigurationContext = React.createContext();
ConfigurationPanel.jsx
import React, { Component } from 'react'
import { Header, Menu, Grid } from 'semantic-ui-react'
import ConfigurationSection from './ConfigurationSection.jsx'
import {ConfigurationContext} from './configuration-context.js'
class ConfigurationPanel extends Component {
constructor(props) {
super(props)
this.state = {
activeItem: '',
configuration: {
/* the configuration values */
banana: (data) => /* set the configuration values with the passed data */
}
}
}
handleItemClick = (e, { name }) => this.setState({ activeItem: name })
render() {
return (
<ConfigurationContext.Provider value={this.state.configuration}>
<Grid.Row centered style={{marginTop:'10vh'}}>
<Grid.Column width={15} >
<div className='configuration-panel'>
/* SOME BUGGED CODE */
<div className='configuration-section-group'>
{this.props.data.map((section, i) => <ConfigurationSection key={i} {...section} />)}
</div>
</div>
</Grid.Column>
</Grid.Row>
</ConfigurationContext.Provider>
)
}
}
ConfigurationItem.jsx
import React, { Component } from 'react'
import { Input, Dropdown, Radio } from 'semantic-ui-react'
import {ConfigurationContext} from './configuration-context.js'
class ConfigurationItem extends Component {
static contextType = ConfigurationContext
constructor(props) {
super(props)
}
handleChange = (e, data) => this.context.banana(data)
itemFromType = (item) =>{
switch (item.type) {
case "toggle":
return <div className='device-configuration-toggle-container'>
<label>{item.label}</label>
<Radio name={item.name} toggle className='device-configuration-toggle'onChange={this.handleChange} />
</div>
/* MORE BUGGED CODE BUT NOT INTERESTING*/
}
}
render() {
return this.itemFromType(this.props.item)
}
}
So, at the end i've a ConfigurationContext that is just a declaration, everything is inside the parent state.
The thing that i don't like is putting the banana function inside the state (it will have more logic that just logging it)
What do you think about it?
Any suggestion is appreciated.
Thanks
banana is just a regular function and you do not have to put it in the state, just do:
class ConfigurationPanel extends Component {
banana = data => console.log(data)
...
render() {
return (
<ConfigurationContext.Provider value={{banana}}>
...
}
After that you can use this.context.banana(data) as normal.
I have small asynchronous problems with render method.
I init my state variable textInRenameTodoPopOverInput with a props variable.
The problem is that when my props variable is changed, the state variable always remains at the initially given value (for this example it will be 'dummyText') so that when the value of my props this.props.todoToRename.text changes, the console always returns :
>> Props in Modal {this.props.todoToRename.text} // Here the props has the expected new value
>> State in modal dummyText // But the state is not refresh and keep 'dummyText'
here is my code that I simplified :
import React from "react";
export default class RenameTodoPopOver extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
textInRenameTodoPopOverInput: this.props.todoToRename.text,
}
}
handleChange = (event) => {
this.setState({ textInRenameTodoPopOverInput: event.target.value });
}
render() {
console.log('Props in Modal ' + this.props.todoToRename.text);
console.log('State in modal ' + this.state.textInRenameTodoPopOverInput);
return (
<input type="text" defaultValue={this.state.textInRenameTodoPopOverInput} onChange={this.handleChange} />
);
}
}
Do you have any idea what my mistake is or how i can resolve this problem ?
You should use getDerivedStateFromProps to update the component state based op props.
export default class RenameTodoPopOver extends React.Component {
state = { textInRenameTodoPopOverInput: '' }
static getDerivedStateFromProps(nextProps){
if(nextProps.todoToRename.text){
return {
textInRenameTodoPopOverInput: nextProps.todoToRename.text
}
}
}
handleChange = (event) => {
this.setState({ textInRenameTodoPopOverInput: event.target.value });
}
render() {
console.log('Props in Modal ' + this.props.todoToRename.text);
console.log('State in modal ' + this.state.textInRenameTodoPopOverInput);
return (
<input type="text" defaultValue={this.state.textInRenameTodoPopOverInput} onChange={this.handleChange} />
);
}
}
some problems with your code:
1. you are using the props and assigning it to the state which is bad.
2. you can shift the "state" to the parent component and let the parent component send the text and handle function as props
//in the parent component
import React from "react";
export default class ParentRenameTodoPopOver extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
textInRenameTodoPopOverInput: this.props.todoToRename.text,
}
}
handleChange = (event) => {
this.setState({ textInRenameTodoPopOverInput: event.target.value });
}
render() {
return (
<RenameTodoPopOver
textInRenameTodoPopOverInput=
{this.state.textInRenameTodoPopOverInput}
handleChange = {this.handleChange}
/>
);
}
}
export default ParentRenameTodoPopOver
now in the child component get the state and handle from the parent as props and work on it.
//in the child component
import React from "react";
const RenameTodoPopOver = (props)=>
return (
<input
type="text"
defaultValue={props.textInRenameTodoPopOverInput}
onChange={props.handleChange} />
)
}
export default RenameTodoPopOver;
while designing components in react, it is easier to write if we divide the components into purely funtional and purely presentational components. Here the parent component is purely functional i.e., it has all the logic on the other hand, the child component is purely functional, it has not logic and it gets all functions and state passed to it from the parent by props.
The react view is not updated (the render is never called) but the reducer was invoked.
I have the following:
1). the react view: I a field in the root state to determent if I need to show "TodoList" or "HousingInfo"
export default class RightPane extends React.Component{
static contextTypes = {
store: React.PropTypes.object
}
render(){
let store = this.context.store;
let curPage = store.getState()["mainRightPanePage"].currentPage;
return (
<div>
{(store.getState()["mainRightPanePage"].currentPage==="TodoList") && <TodoList/>}
{(store.getState()["mainRightPanePage"].currentPage==="HousingInfo") && <HousingInfo/>}
</div>
)
}
}
2). the action dispatching in another component
export default class LeftPane extends React.Component{
static contextTypes = {
store: React.PropTypes.object
}
handleink(pageId, e) {
e.preventDefault();
let store = this.context.store;
store.dispatch({'type':'switchPage', 'pageId':pageId});
...
}
3). the reducer: the following reducer was invoked
const mainRightPanePage = (state = {'currentPage':'TodoList'}, action) => {
switch (action.type) {
case 'switchPage':
return Object.assign({}, state, {
currentPage: action.pageId
})
default:
return state
}
}
export default mainRightPanePage
What did I miss?
thanks
In your example the RightPane component is not aware that Redux state was updated because you haven't subscribed to Redux state changes. You can subscribe to Redux store directly using subscribe method or you can connect your components to Redux store using connect method from React-Redux (recommended):
import {connect} from 'react-redux';
...
class RightPane extends React.Component{
...
render(){
let currentPage = this.props.currentPage;
return (
<div>
{(currentPage === "TodoList") && <TodoList/>}
{(currentPage === "HousingInfo") && <HousingInfo/>}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
currentPage: state.mainRightPanePage.currentPage
}
};
export default connect(
mapStateToProps
)(RightPane);
I am new react-redux and I am trying to save to props the value entered in from a react-redux-form textbox by using the onChange event which can be passed to another component
My code snippet for the textbox is
<ListItemContent>
<Control component={Textfield} model="somemodel" label="MyLabel"
onChange={this.props}/>
</ListItemContent>
How can I save this value and make this available to other components?
EDIT I have it partly working:
<ListItemContent>
<Control component={Textfield} model="somemodel" label="MyLabel"
onBlur={this.onChangeOfValue}/>
</ListItemContent>
onChangeOfValue = (event) =>
{
this.setState({ newValueToPassAlong: event.target.value}); //newValueToPassAlong is set in constructor
};
.....
let mapStateToProps = (state) => {
return {newValueToGive: state.newValueToPassAlong} //This is undefined
};
export default connect(mapStateToProps)(form)
Further, my componentWillReceiveProps(nextProps) is not being fired when the other component's state changes.
// YOUR TEXTFIELD COMPONENT
import React, { Component } form 'react';
import { reduxForm, Field } from 'redux-form';
import {passValueToOtherComponent} from '../actions/your-actions-index-file';
import { connect } from 'react-redux';
import ListItemContent form 'list-item-content';
class TextField extends Component {
constructor(props) {
super(props);
this.state = {
textFieldValue: '',
}
this.onInputChange = this.onInputChange.bind(this);
}
onInputChange(event) {
var newValue = event.target.value;
this.setState({textFieldValue: newValue});
//when input changes
//call action to update global state...
this.props.passValueToOtherComponent(this.state.textFieldValue)
}
render() {
<form>
<ListItemContent>
<Control component={Textfield} model="somemodel" label="MyLabel"
onChange={this.onInputChange} value={this.state.textFieldValue}/>
</ListItemContent>
</form>
}
}
//ReduxForm wrapper
const wrappedReduxForm = connect(null, {passValueToOtherComponent})(TextField)
export default reduxForm({
form: 'TextField'
})(TextField)
// actions/your-actions-index-file.js
//create an action which will call to update global state
export const NEW_VALUE = "NEW_VALUE"
export function passValueToOtherComponent(value) {
return {
type: CREATE_POST,
payload: value,
}
}
//YOUR NewValue Reducer
//reducer_new_value.js
//create reducer which will accept payload data
import {NEW_VALUE} from '../actions/your-actions-index-file';
const INITIAL_STATE = {
valueToPass: null
};
export default function (state = [], action) {
console.log('Action...' action);
switch (action.type) {
case NEW_VALUE:
return { ...state, valueToPass: action.payload.data}
break;
default:
}
}
//Your Root Reducer
//Because you may have lots of state to manage, a rootReducer is awesome in managing it all
import { combineReducers } from 'redux';
import NewValueReducer from './reducer_new_value';
const rootReducer = combineReducers({
// state: (state = {}) => state
value: NewValueReducer,
});
export default rootReducer;
//finally pass this desired value to your desired Component
import React, {Component} from 'react';
import { connect } from 'react-redux';
class OtherComponent extends Component {
render() {
return (
<div>
<input value= {this.props.texFieldValue}>
</div>
)
}
}
function mapStateToProps(state) {
return { textFieldValue: state.value.valueToPass }
}
export default connect(mapStateToProps)(OtherComponent);
This is something I just typed out. Not sure if it will work, but it covers actions, reducers, and updating values from one component to anohter. Of course, this is a crazy way to do it using react redux. I'm not sure how efficient it would be to call this action on every input change. You might be better off passing the current value of state as a prop to your desired component.
If you have some more questions, I'd be happy to help or point you to some other resources.
Say I had a simple inputfieldcomponent like so:
import React, {PropTypes, PureComponent} from 'react';
import {render} from 'react-dom';
import {connect} from 'react-redux';
import updateInput from '../../actions/inputActions';
require('./SimpleInput.sass');
export class SimpleInput extends PureComponent {
constructor(props, context) {
super(props, context);
}
handleChange (event) {
this.props.updateField(event.target.value);
}
renderField () {
return (
<input type="text" value={this.props.value || ''} onChange={this::this.handleChange} placeholder={this.props.initial_value}/>
)
}
render () {
return(
<span>
{this.renderField()}
</span>
)
}
}
const mapStateToProps = (state) => {
return {value: state.value.value}
}
const mapDispatchToProps = (dispatch) => {
return {
updateInput: (value) => dispatch(updateInput(value))
};
};
AddressInput.propTypes = {
initial_value: PropTypes.string
};
AddressInput.defaultProps = {
initial_value: "What's the value?"
};
export default connect(mapStateToProps, mapDispatchToProps)(SimpleInput);
I then render two instances:
</SimpleInput initial_value='blah'/>
</SimpleInput>
However, when this is rendered, any update to one of the two fields updates both of them (due to redux only allowing for a single state).
What is the canonical way to approach this problem?
Here's one approach Abraham and giving an example to what Dan commented on. Check out this jsBin demonstrating the concept of a component (here it's SimpleInput) keeping its own internal state (such as a default placeholder) while still interacting with a Container (listening to onChange). While the example doesn't use Redux (for simplicity of creating the demo) you could easily substitute onChange handles with action dispatchers.
class SimpleInput extends React.Component {
constructor(props) {
super(props);
this.state = {
defaultPlaceholder: 'Default Placeholder'
}
}
render() {
return (
<input
value={this.props.value}
placeholder={this.props.placeholder || this.state.defaultPlaceholder}
onChange={this.props.onChange || ()=>{} }
/>
)
}
}
class SimpleInputContainer extends React.Component {
constructor(props) {
super(props);
this.changeInput1 = this.changeInput1.bind(this);
this.changeInput2 = this.changeInput2.bind(this);
this.state = {
input1: 'foo',
input2: 'bar',
}
}
changeInput1(e) {
this.setState({
input1: e.target.value
})
console.log(this.state);
}
changeInput2(e) {
this.setState({
input2: e.target.value
})
}
render() {
return (
<div>
<SimpleInput value={this.state.input1} onChange={this.changeInput1} />
<br />
<SimpleInput value={this.state.input2} onChange={this.changeInput2} />
<br />
<SimpleInput />
<br />
<SimpleInput placeholder={'Explicit Placeholder'} />
</div>
);
}
}
ReactDOM.render(<SimpleInputContainer />, document.getElementById('app'));
One of my use cases was an address lookup with an input managing it's value internally (no redux) and a lookup button emitting the value of the input via onClick={this.props.onSave} and in the Container handling all side effects/redux actions etc.
Lastly in your demo code, you're rerendering SimpleInput unnecessarily, which looks like a code smell. Hope that helps.