It must be pretty regular issue.
I'm passing props down to the children and I'm using it there to request to the endpoint. More detailed: I'm clicking on the list item, I'm checking which item was clicked, I'm passing it to the child component and there basing on prop I passed I'd like to request certain data. All works fine and I'm getting what I need, but only for the first time, ie. when refreshing page incoming props are gone and I cannot construct proper URL where as a query I'd like to use the prop value. Is there a way to preserve the prop so when the page will be refresh it will preserve last prop.
Thank you!
(You might want to take a look at: https://github.com/rt2zz/redux-persist, it is one of my favorites)
Just like a normal web application if the user reloads the page you're going to have your code reloaded. The solution is you need to store the critical data somewhere other than the React state if you want it to survive.
Here's a "template" in pseudo code. I just used a "LocalStorage" class that doesn't exist. You could pick whatever method you wanted.
class Persist extends React.Component {
constuctor(props) {
this.state = {
criticalData = null
}
}
componentDidMount() {
//pseudo code
let criticalData = LocalStorage.get('criticalData')
this.setState({
criticalData: criticalData
})
}
_handleCriticalUpdate(update) {
const merge = {
...LocalStorage.get('criticalData')
...update
}
LocalStorage.put('criticalData', merge)
this.setState({
criticalData: merge
})
}
render() {
<div>
...
<button
onClick={e => {
let update = ...my business logic
this._handleCriticalUpdate(update) //instead of set state
}}
>
....
</div>
}
}
By offloading your critical data to a cookie or the local storage you are injecting persistence into the lifecycle of the component. This means when a user refreshes the page you keep your state.
I hope that helps!
Related
I am building on sample 16 from Github/Webchat to build a webpage with a webchat interface.
https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/16.customization-selectable-activity
The react app consists off different .js files to build the website( Webchat.js, Instuctor.js, App.js, Index.js) and I can't provide file Inspector.js with the data I gathered in file Webchat.js.
I can't find the right code to read the value of a variable from file Webchat.js in file Inspector.js.
I want to build a Webpage where I have on the left side a Chatbot (BotFrameWork) running, and next to it a table running which shows data that has been collected by the chatbot.
I tried answers from
how to get a variable from a file to another file in node.js
but doesn't work.
I tried to get the state of Webchat but gives only undefined.
Example:
(webchat.js) I fetched data from the bot (like [link]How to create a side window or iframe that shows data that was gathered by a Web-Chat in one web page?) and saved it in a state variable 'test'.
(instructor.js) I want to show that data e.g. in a label that is getting updated when new data comes in. How do I get access now to the value of 'test' that is created in another file?
What doesnt work:
in instuctor.js:
var test2 = require ('./webchat');
Console.log(test2.state.test) //this is how I imagine it to work --> undefined
with require I only get an object that has a 'name' variable 'Webchat' and which i can get out with: console.log(test2.default.name);
React only supports one-way data binding, so if you want to share a variable between multiple components, you need to elevate the state to the parent and pass the variable and change handlers to the children as props.
In the example below, Parent has two children: ChildA and ChildB. We could keep myValue in ChildA's state, but then ChildB wouldn't be able to access it, so we elevate myValue to the parent and pass it to both children as props. We also, pass a change handler to ChildB so it can update the value when the user clicks it.
import React from 'react';
const ChildA = ({ myValue }) => (
<div>
{
myValue
? <h1>Hello World</h1>
: <h1>Goodbye!</h1>
}
</div>
);
const ChildB = ({ myValue, handleMyValueChange}) => (
<button onClick={ () => handleMyValueChange(false) } disabled={ myValue }>
Click Me!
</button>
);
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { myValue: true }
}
render() {
return (
<div>
<ChildA myValue={this.props.myValue}/>
<ChildB myValue={this.props.myValue} handleMyValueChange={ handleMyValueChange }/>
</div>
)
}
handleMyValueChange = myValue => {
this.setState({ myValue });
}
}
In terms of the sample you referenced, the parent class is App and the two children are ReactWebChat and Inspector. I would recommend elevating the state of your variable to the parent - App - and pass it as a prop to the Inspector class. Then you can add a custom store middleware to ReactWebChat that updates your variable when the bot sends an update event. For more information on how to configure your bot to send update events and how to make Web Chat listen for them, take a look at this StackOverflow Question.
Hope this helps!
I know there are some similar topics but none seems to be in the same direction of what I'm trying to do, thus a new thread.
I have a component that displays a list of keys, each with a checkbox attached to the string. In addition, I have a button that supposedly calls an API with all keys selected and delete these keys.
Several things I'm trying to achieve:
checking a check box enables the delete button
click the delete button should send a POST to API, the list should then reload
Since the list is reloaded, all checkbox should be unselected, thus the delete button is once again disabled
there's another button outside of this function that checks for the length of the list as well, which I don't know how to associate with this list if I fetch the list in the component.
I'm facing the problem which I don't know how to make the button and the checkboxes associate to each other. I tried using state with a checked state, which is a boolean, but that's only one boolean and cannot record several keys. I think using an array would work? Then again I'm not sure how to properly append or remove the key checked.
my code looks like
class AppList extends Component {
constructor() {
super();
this.state = {
checked: [],
apps: []
};
this.handleChecked = this.handleChecked.bind(this);
}
componentDidMount() {
fetch("some_url", {
method: 'post',
body: JSON.stringify({"user": "some_email"}),
headers: {'Content-Type': ' application/json'}
})
.then(res => res.json())
.then(
(result) => {
this.setState({apps: JSON.parse(result)});
},
(error) => {
console.log("error", error);
}
);
}
handleDeleteKey = (event) => {
// fetch(I'll worry about this later)
this.setState({checked: false});
console.log("delete!!!!");
}
handleChecked () {
this.setState({checked: !this.state.checked});
}
render () {
const apps = this.state.apps.map((app) =>
<div>
<input type="checkbox" onChange={this.handleChecked} />
{` ${app}`}
</div>
);
return (
<div>
<h4>Client Key List:</h4>
{this.state.apps.length > 0 ? <ul>{apps}</ul> : <p>No Key</p>}
{this.state.apps.length > 0 ? <button className="api-instruction-button" onClick={this.handleDeleteKey}>Delete Selected Key(s)</button> : null}
</div>
);
}
}
export default AppList;
I feel like my design is completely wrong but I don't know how to fix it. It seems like there are so many states to be passed around and nothing is the outermost, almost a cyclic dependency.
Anyone had any experience dealing with this problem? It seems like it's a common user action but I can't figure it out.
EDIT: after digging it a bit more, it seems like I need to call componentDidMount outside of the AppList. It should be in the component that uses AppList, let's call it MainApp.
MainApp calls componentDidMount which is the same as the one in AppList. The one in AppList gets removed, and the keys are passed to AppList as props.
I have trouble handling the clicking event. It seems like the component is always updating, so if I want to append the clicked key to the array, it wouldn't work. The same call will be made again and again.
Since there's another button in MainApp that requires the list of keys, I can't just pass the call into AppList. However, updating in AppList should update the MainApp as well. How does it work? I'm so confused
EDIT2:
https://codesandbox.io/s/7w2w11477j
This recreation should contain all functions I have so far, but I can't get them to work together.
Again my task is simply:
I have a list of strings, each with a checkbox
checking the checkbox selects the specific string
There's a button that I can click to delete these entries in my db by calling an API
Is refreshing the MainApp needed in this case? Otherwise I need to delete the strings in frontend so they don't display after the delete button is pressed
Here's what I believe you were going for: https://codesandbox.io/s/w23wv002yw
The only problem that made yours not work properly was you were just getting a little jumbled with where to put everything.
Contents:
The MainApp.js will only contain the apps and a method for deleting them in the backend. Other than those two methods, nothing else really concerns the MainApp.js file.
The AppList.js will contain all the methods that update its own checked state, the delete button itself, and a method to clear the checked state on delete.
Processes:
First, MainApp.js will load and remount with a backend api pull and populate its apps state. Once it's finished that, it will pass it on to AppList.js. From there, AppList.js will render that list as a multi-select field onscreen. The user can then select or deselect any of the options. As an option is selected, its index is pushed to the checked state and organized in ascending order.
(ordering the array isn't that necessary, but I figured it would help if you wanted to retool it sometime down the road)
When one or more option is selected, a delete button will appear. When the user clicks the delete button, AppList.js will call the delete function passed to it from MainApp.js, then it will clear the current checked state.
I have the below link in one of the components of my project
<Link to={`http://localhost:8080/project/path/items?ids=18,19`}>
{itemCount}
</Link>
It redirects to another component. That component picks up the ids list from url and displays the result
Now my problem is when the ids list gets large enough (tens of thousands) it doesn't work.
I need a better approach of handling this
Is it possible through a post request?
I assume you are using Link from react-router?
The to props in Link isn't limited to string values. You can pass a state as well like this:
<Link
key={i.id}
to={{
pathname: `/someUrl`,
state: { someValue: 'asdad', someArray: [1, 2, 3] }
}}
>
Then at the destination route, you can access the state from this.props.location.state. This value only exist for that particular browser history push. If you type the url directly to the browser, this.props.location.state.someValue will be undefined.
I think it would be easier to handle if you save your id's in local state for the component that's supposed to link to the other component.
this.state = {
ids = [];
}
I'm not sure how you get your id's, normally you get them from props or an API. But just to make an example on how to add ids to your state:
componentDidMount() {
this.setState({
ids: [18, 19, 20, 21]
})
}
This way you'll have them all in one place, and you can send them to the other component through props.
<MyComponent ids={this.state.ids.toString().split(',').join()} />
To create your new component (don't know how experienced you are):
export default class MyComponent extends React.Component {
constructor(props) {
super(props)
}
render() {
let itemCount = {this.props.ids.length}
return(
<Link to={`http://localhost:8080/project/path/items?ids={this.props.ids}`}>
{itemCount}
</Link>
)
}
}
It's important though to know that every element that's supposed to be shown on screen, needs to be kept in the client devices memory.
Therefor it'll be challenging rendering thousands of element without noticing performance issues. You should prepare yourself for showing a batch of all the id's if you're noticing that the app is getting slow or takes a long time to load.
Two common ways of "batching" a long list is either pagination or lazy-loading.
I'm working on integrating Redux in an already finished SPA with ReactJS.
On my HomePage I have a list of the 4 newest collections added which on render, I fetch with axios from my database. These are then saved in Redux Store and displayed on the React UI.
My mapStateToProps look something like this:
const mapStateToProps = (state) => ({
credentials: credentials(state),
collections: collections(state)
});
Where credentials is irrelevant and collections is:
const collections = (state) => {
if (state.collectionsHomeViewReducer.fetching === true) {
return {
fetchingCollections: true
}
}
else if (state.collectionsHomeViewReducer.data) {
const response = state.collectionsHomeViewReducer.data;
return {
collections: response.collections,
fetchedCollections: true,
fetchingCollections: false
}
}
else if (state.collectionsHomeViewReducer.fetched === false) {
return {
fetchedCollections: false,
fetchingCollections: false
}
}
};
What is it I want to do:
Update the store state every time another client, or the current client, adds a new collection. Moreover, I do not wish for the UI to update immediately after I dispatch(action), I want it to update when a user refreshes the page or when he navigates to another view and returns ( I believe what I'm trying to say is when componentDidMount is called ).
What have I achieved so far:
By using socket.io, I
socket.emit("updateCollectionsStore")
socket.on("updateCollectionsStore")
and
socket.broadcast.emit("updateCollectionsStore")
in their respective places in the application. The final call of
socket.on("updateCollectionsStore")
after the broadcast, is in the main file of the page, app.jsx where the store is also located. The function there looks like this:
socket.on("updateCollectionsStore", () => {
store.dispatch(getCollectionsHomeView());
});
The store is updated and everything works fine, as viewed from the Redux Dev Tools.
What I can't seem to figure out is to tell the props not to change due to the fact that mapStateToProps is called every time an action is dispatched.
Why do I need this: The HomePage can deal with a continuous UI update and data fetching but I also have a page ReadAllPage where you can real all collections. The problem is if there will always be the newest post on the top, whenever a new one is added, the current one is pushed downwards. In case somebody had the intent to click the one that was pushed down, now he might have accidentally clicked the one that took its place, which is not wanted.
What else should I do different or further to achieve the result I want?
Thank you.
According to your needs, I would have two properties in the state. First is that is currently visible on the HomeView and the second is that is updated via sockets. Once a user navigates to the HomeView you can just replace the first collection with the second one.
Background
I am building an app with the following details
react
react-router
redux
it is universal javascript
node js
Problem
When routing with the Link tag from component to component it works perfectly. It calls the data that the component requires and renders the page. But when I click on a Link that uses the same component as the current one all I see is the url change.
Attempts
Things I have tried to get this to work.
Attempt 1
So far I have tried the steps in this question but the solution wont work for me. This was the code I implemented
componentWillReceiveProps(nextProps) {
if (nextProps.article.get('id') !== this.props.article.get('id')) {
console.log('i got trigggerd YESSSSSSSSSSSSSSS');
}
}
But the variable nextProps is always the same as the current props.
Attempt 2
I decided to call the same code I use in componentWillMount but that didn't work either.
componentWillMount() {
let { category, slug } = this.props.params;
this.props.loadArticleState({ category, slug });
}
It just creates an infinite loop when I put this into componentWillReceiveProps.
Conclusion
I belief the problem is clicking the link never calls the data associated with it. Since the data is loaded with
static fetchData({ store, params }) {
let { category, slug } = params;
return store.dispatch(loadArticleState({ category, slug }));
}
Any help is appreciated.
Solution I Used
I created a function to test if the previous data is the same as the changed data.
compareParams(prevProps, props) {
if (!prevProps || typeof prevProps.params !== typeof props.params) {
return false;
}
return Object.is(props.params, prevProps.params);
}
So this tests
are there any previous props?
and then if the props are equal to the previous props?
then return false if there are if this is the case
if not then we see compare props and previous props parameters
In ComponentDidUpdate
In the compoonentDidUpdate we use this function to determine if the data should be updated
componentDidUpdate(prevProps) {
if (this.compareParams(prevProps, this.props)) {
return;
}
this.props[this.constructor.reducerName](this.props.params);
}
Conclusion
This code updates the body of a page that uses the same react component if it receives new data.
maybe you can try use onChange event on Route component, check Route API and then signal to child component that refresh is needed...