How to update a stream of React components? - reactjs

I have a simple functional component
const Hello = (props) => <div>{props.name}</div>
How can I supply a stream of values to the props parameter and make the component update in reactive fashion, consuming the stream? So that every time the new value comes from the stream, I get the component updated with that value.
Basically I am looking for a way to manually re-render the component.

You can use two basic methods:
Component rerendering, which you mentioned
State system: https://facebook.github.io/react/docs/state-and-lifecycle.html
The second way could look like this:
const Hello = (props) => <div>{props.name}</div>
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
name: ''
};
}
componentDidMount() {
source.on('event', name => {
this.setState({name});
});
}
render() {
return <Hello name=this.state.name/>;
}
}

Related

ReactJS Change Sibling State via Parent

My React structure is
- App
|--SelectStudy
|--ParticipantsTable
In SelectStudy there is a button whose click triggers a message to its sibling, ParticipantsTable, via the App parent. The first Child->Parent transfer works. But how do I implement the second Parent->Child transfer? See questions in comments.
App
class App extends Component {
myCallback(dataFromChild) {
// This callback receives changes from SelectStudy Child Component's button click
// THIS WORKS
alert('SelectStudy Component sent value to Parent (App): ' + dataFromChild.label + " -> " + dataFromChild.value);
// QUESTION: How to Update State of ParticipantsTable (SelectStudy's Sibling) next?
// ........................................................
}
render() {
return (
<div className="App">
<SelectStudy callbackFromParent={this.myCallback}></SelectStudy>
<ParticipantsTable></ParticipantsTable>
</div>
);
}
SelectStudy
class SelectStudy extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: [],
selectedStudy: null,
isButtonLoading: false
};
this.handleButtonClick = this.handleButtonClick.bind(this);
}
render() {
const { error, isLoaded, items, itemsForReactSelect, selectedStudy, isButtonLoading } = this.state;
return <Button onClick={this.handleButtonClick}>Search</Button>;
}
handleButtonClick = () => {
this.props.callbackFromParent(this.state.selectedStudy);
}
}
ParticipantsTable - this needs to receive a certain variable, e.g. study in its State
class ParticipantsTable extends React.Component {
constructor(props) {
//alert('Constructor');
super(props);
// Initial Definition of this component's state
this.state = {
study: null,
items: [],
error: null
};
}
// THIS METHOD IS AVAILABLE, BUT HOW TO CALL IT FROM App's myCallback(dataFromChild)?
setStudy = (selectedStudy) => {
this.setState({study: selectedStudy});
}
render() {
return ( <div>{this.state.study}</div> );
}
}
The state should live definitively at the App level, not in the child. State needs to live one level above the lowest common denominator that needs access to it. So if both SelectStudy and ParticipantsTable need access to the same bit of state data, then it must live in their closest common ancestor (or above).
This is a core concept of React, known as "lifting state up", so much so that it has its own page in the official React documentation.
In your case, it would look something like this. Notice how state lives in only one place, at the <App /> level, and is passed to children via props.
import React from 'react';
class App extends React.Component {
// State lives here at the closest common ancestor of children that need it
state = {
error: null,
isLoaded: false,
items: [],
selectedStudy: null,
isButtonLoading: false
};
myCallback = (dataFromChild) => {
this.setState(dataFromChild);
};
render() {
return (
<div className="App">
{/* State is passed into child components here, as props */}
<SelectStudy data={this.state} callbackFromParent={this.myCallback}></SelectStudy>
<ParticipantsTable study={this.state.selectedStudy} />
</div>
);
}
}
class SelectStudy extends React.Component {
handleButtonClick = () => {
// Here we execute a callback, provided by <App />, to update state one level up
this.props.callbackFromParent({ ...this.props.selectedStudy, isButtonLoading: true });
};
render() {
const { error, isLoaded, items, itemsForReactSelect, selectedStudy, isButtonLoading } = this.props.data;
return <Button onClick={this.handleButtonClick}>Search</Button>;
}
}
// This component doesn't need to track any internal state - it only renders what is given via props
class ParticipantsTable extends React.Component {
render() {
return <div>{this.props.study}</div>;
}
}
I think what you need to understand is the difference between state and props.
state is internal to a component while props are passed down from parents to children
Here is a in-depth answer
So you want to set a state in the parent that you can pass as props to children
1 set state in the parent
this.state = {
value: null
}
myCallback(dataFromChild) {
this.setState({value: dataFromChild.value})
}
2 pass it as a prop to the children
class ParticipantsTable extends React.Component {
constructor(props) {
super(props);
this.state = {
study: props.study,
items: [],
error: null
};
}
Also, although not related to your question, if you learning React I suggest moving away from class-based components in favour of hooks and functional components as they have become more widely used and popular recently.

React+ sending entire state object to another component

pretty simple but confused,
i have one parent component with state as
this is my
this.state = {
todoName: "",
todoList: [],
"isAvail":true,
"IsArchive":false
};
and inside my parent component i am calling child component
<ChildComponent data= {this.state} />
but in childCompoenent inside the render i try to call like
const isAvail = this.props.data.isAvail;
const isArchieve = this.props.data.isArchieve;
and inside retrun if i call like this am {isAvail} am not getting and am getting error
Objects are not valid as a React child (found: object with keys {todoName, todoList, isAvail, IsArchive}). If you meant to render a collection of children, use an array instead.
But somehow i need to get full object..How is it possible
You may call like this one
render() {
const {isAvail, isArchieve} = this.props.data;
return(<div><p>{isAvail}</p></div>);
}
class Parent extends React.Component{
constructor(props){
super(props)
this.state= {
todoName: "passed from parent", todoList: [], "isAvail":true, "IsArchive":false
}
}
render(){
return <Child data={this.state}/>
}
}
const Child = (props) => <div>{props.data.todoName}{console.log(props)}</div>
Live Demo
Do you know ContextAPI & you want to send data without using props drilling useContextAPI. In some cases we can use state values in to childcomponents also in using context API.
In your parent component create context
const ParentState = React.createContext()
render() {
<ParentState.Provider value={{ data: this.state }}>
<Childcomponent/>
</ParentState.Provider>
}
In Child component import Context and use
render() {
return(
<ParentState.Consumer>
{
parentState => (<div><p>{parentState.data.isAvail}</p></div>)
}
</ParentState.Consumer>
)
}

How to keep track of a value after I update the component in react 16.3

I just update to the react 16.3. I have a value to keep tracking of a value that I need to post to the server. I want to save this.value after some props changed. I found out that a lot of life cycle functions are deprecated. And I cannot save the value into redux before rendered. Could anyone give me a good way to handle it? Thanks.
class Foo extends Component {
constructor(props) {
super(props);
this.value = {};
}
render() {
return (
//some other components
<Bar onChange={value => this.value = value} />
)
}
}
I would most likely handle it with this
class Foo extends Component {
state = {
text: ''
}
render() {
return (
//some other components
<Bar onChange={value => this.setState({text:value})} />
)
}
}
Keep in mind this is ES7 way to do it. A bit cleaner than doing it in constructor(). If you don't use new syntax just initiate state in constructor as,
constructor(props){
super(props)
this.state = {
text: ''
}
}
if you would like to get your hands dirty more with handling value that user is giving could also pass onChange value to own function and setState on there. Many prefer it that way.
e.g.
handleChange = (text) => {
// Some amaizing text manipulation
this.setState({text})
}
render() {
return (
//some other components
<Bar onChange={this.handleChange} />
)
}
and with redux dispatch function
constructor(props) {
super(props)
/**
* Bind funtions
*/
const { dispatch } = props
this.patchReservation = params =>
dispatch(ActionCreators.patchReservation(params))
}
Then you just attach e.g. this.patchReservation to onChange -listener. ActionCreators is one of my import's which contains my Redux action -functions.
Cheers!

React dumb component with UI state

I want to build a select input component with React.
The select should be dumb component as it's only a UI Component,
but it also have it's own state (Whether to show the options list, or not)
How should I manage this state?
return (
const Select = (props) => {
<div>
<label>{placeholder}</label>
{/*some toggle state*/ && <div>props.children</div>}
</div>
}
)
thanks!
You should not get too confused by the fact that "it's only a UI component". Anything that has an internal state should be a class.
Your code, a dropdown, is my go-to example of when you should use internal state.
Manage your state with setState().
Now your component is stateless, but you need a stateful.
For example:
class Select extends React.Component {
constructor(props) {
super(props);
this.state = {value: '', toggle: false};
}
render() {
return (
<div>
<label>{placeholder}</label>
{this.state.toggle && <div>this.props.children</div>}
</div>
);
}
}
And you should change state with setState function.
For more information, check this article.
According to your code, what you are rendering is a stateless component, so it will not have any state.
What you can do is pass the state from the parent to this component like so:
constructor(props) {
this.state = { showDumbComponent:true }
}
render() {
<DumbComponent show={this.state.showDumbComponent} />
}

One React Component. Two HOCs. How to make setState correctly update props?

Given a standard compose function and a 'div' Component, how would you write the two HOCs such that:
The 'div' element starts as a 20px green box, then on click, becomes a 50px blue box.
The concerns of - a: merging state with props, and b: triggering a state change, are handled by separate HOCs.
the updater HOC maps state to props, and sets a default state
the dispatcher HOC accepts a function to get the new state on click
The example below works to get a green box, and correctly fires the handler. The update only happens in the state of the Dispatcher HOC's state. The updater HOC's state remains unchanged, as do its props.
I'm really curious to understand what's happening. Flipping the two HOCs' order in compose causes the handler not to be set. Since they both merge in {...this.props}, that doesn't make sense to me. Guessing there's something I don't understand about how multiple HOCs merge props and state.
const HOCDispatcher = myFunc => BaseComponent => {
return class Dispatcher extends React.Component {
constructor(props,context){
super(props,context);
this.handlerFn = (event)=>{this.setState(myFunc)}
}
render(){
return createElement(BaseComponent,{...this.props,onClick:this.handlerFn});
}
}
}
const HOCUpdater = defaultState => BaseComponent => {
return class Updater extends React.Component {
constructor(props,context){
super(props,context);
this.state = Object.assign({},defaultState,this.state);
}
render(){
return createElement(BaseComponent,{...this.props,...this.state});
}
}
}
const MyComponent = compose(
HOCDispatcher(()=>({
style:{width:'50px',height:'50px',background:'blue'}
})),
HOCUpdater({
style:{width:'20px',height:'20px',background:'green'}
}),
)('div');
If you try to simplify or compile your code in a way to a less complicated structure you can understand it better:
The initial version of MyComponent
const MyComponent= class Dispatcher extends React.Component {
constructor(props,context){
super(props,context);
this.handlerFn = (event)=>{this.setState({
style:{width:'50px',height:'50px',background:'blue'}
})}
}
render(){
return <HOCUpdater onClick={this.handlerFn}/>
}
}
Where HOCUpdater also renders as:
class Updater extends React.Component {
constructor(props,context){
super(props,context);
this.state = {
style:{width:'20px',height:'20px',background:'green'}
};
}
render(){
return <div style:{width:'20px',height:'20px',background:'green'}/>;
}
}
Thus rendering the green box.
After triggering the click
const MyComponent= class Dispatcher extends React.Component {
constructor(props,context){
super(props,context);
this.handlerFn = (event)=>{this.setState({
style:{width:'50px',height:'50px',background:'blue'}
})};
this.state= {
style:{width:'50px',height:'50px',background:'blue'}
};
}
render(){
return <HOCUpdater onClick={this.handlerFn}/>
}
}
If you pay attention to the render, it's still the same because this.props has not changed and it is still empty. Thus no change to the style of the box whereas the state of the Dispatcher is changed!
Did you see where you went wrong? Well, just change this.props to this.state in the Dispatcher and you'll see the magic happen.
But wait, there's more!
What happens if you have a line of code like this?
createElement('div',{
style:{width:'50px',height:'50px',background:'blue'},
style:{width:'20px',height:'20px',background:'green'}
});
Well, it still renders the first one (the blue box) but to avoid this try changing the render method of HOCUpdater to this:
return createElement(BaseComponent,{...this.state});
and also add a componentWillReceiveProps method, so your HOCUpdater will look like this:
const HOCUpdater = defaultState => BaseComponent => {
return class Updater extends React.Component {
constructor(props,context){
super(props,context);
this.state = Object.assign({},defaultState,this.state);
}
componentWillReceiveProps(nextProps){
this.setState(nextProps);
}
render(){
return createElement(BaseComponent,{...this.state});
}
}
}

Resources