Page does not retain its state on re rendering - reactjs

In my react app component, I have a table on page with some list of data. On clicking any row on that table it fetches the details of that particular table row via its id and routing to another link like '/parent/:id' using the same parent component. On refreshing the page of single row details, it's taking me back to parent table data i.e on /parent page, but the URL is still /parent/:id.
I want to retain the /parent/:Id page even on refreshing.
Any leads?

Here is a general approach to such scenarios. I use search params in such a way for the url:
parent?rowId=2
Then in the parent component you check if the search param includes the rowId as a string. If so show the detail content and hide parent content, if not, show parent and hide detail content.
Parent Component would be something similar to this:
render () {
const { search } = this.props.location
const shouldDisplayRowDetail = search.includes('rowId')
return (
<div>
{
!shouldDisplayRowDetail &&
<div> Parent content </div>
}
{
shouldDisplayRowDetail &&
<div> Row content </div>
}
</div>
)
}

Related

How should I show editable data after it has been submitted with a form so that I can reuse a form components?

If I was making a website where the user submits some data using a form and then the user is redirected to a page with a unique URL where they can view this data, what is the best way to display this data to user in a way in which they can make updates to it?
To give some context I am using react and right now I am showing the same form again on the view data page, but as the forms have slightly different display options, such as some fields being disabled and some extra buttons I am repeating the same form in two seperate components which seems to go against the react way of doing things.
The way I would personally do it is have all your form elements in a single component and use it for the create and edit page, then just pass a "isCreating" or "isEditing" prop, or something similar. In that component, you can then conditionally disable your inputs.
Component
const SomeForm = ({ isEditing }) => {
const onSubmit = () => {
if (isEditing) {
// Make request to update endpoint
} else {
// Make request to create endpoint
}
};
return (
<>
<form onSubmit={onSubmit}>
<input disabled={isEditing} />
<input />
</form>
</>
);
};
Create Page Usage
<MyForm />
Edit Page Usage
<MyForm isEditing />
Note that if you're doing a ton of conditional logic/rendering based on whether you're creating or editing a form, it might be better to split it into two separate form components. If you're simply disabling some inputs on the edit page and doing different submit logic, then I think this is fine.

How to setup React Router <Link> inside a Datatables.net Cell?

I have a datatables table (https://datatables.net) initialized like this:
$('#example').DataTable({
data: data,
columns: [{
title: "Database ID",
data: 'database_id',
render: function (data, type, row) {
return '<NavLink to="/shop">'+ data +'</NavLink>';
}
}]
});
The NavLink that i have in the code is supposed to render a database cell as a clickable link because of React-Router (This whole function is inside a React component), but the link is not rendering when i run the code.
What i ultimately want is the ability to click on a database cell that will take me to another component by routing into a link like "/shop/id" but i am stuck in it for a long time. Help!
I was facing the same issue and below solution is working for me.
You can directly add anchor tag with href to the path where you want to route upon on the click. But, it will reload your application.
To avoid reloading of the application, try below solution.
When you're initializing the columns dynamically, you can add below code for the column where you want to have a link.
fnCreatedCell: (nTd, data) => ReactDOM.render(
<a
onClick={() => handletableclick(data, props)}>
{data}
</a>, nTd)
Add handletableclick() function/method in your file to handle click event and programmatically through history push the path where you want to route upon the click
function handletableclick(data, props) {
props.history.push("/pathToRoute=" + data);
}
Also,in above code to access history from props, you will need to enclose your component with withRouter
like below :
export default withRouter(YourComponentName);

React App: how to pass a variable to other file and read its value there

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!

Preserve internal state on page refresh in React.js

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!

React rendering html table and reading from html table

This is a conceptual question in that I'm trying to understand the best way to handle tabular data in react without using any special component or library.
I have data in a html table created dynamically in my child component. The data comes from the parent component. Some of the columns have editable content that I trigger with an edit button to re-render a version of the table that has inline text boxes for all rows of the columns that are editable.
When I change the content of the text box, I want to be able to click on my save button and have all the rows get saved.
The save and edit buttons are not inline on the table, but just sit outside the table in my component.
How do I access the html table in my child component from the the parent component to read all the rows and save the values in the textboxes to a data store?
Here is a snippet of code where I'm attempting to build the select list dynamically. I'm having trouble with some syntax errors and it is not working, but it gives an idea of what I'm trying to do.
I'm passing in the category and the transaction id. I want to add the select list to each category cell in every row in my table when the edit mode is selected. The transaction id is my solution for having the index of the current row available on the list by adding 1 to the transaction id. I will then use the selected index - 1 to get the transaction id for updating the corresponding records category. This may be a hack, but I can't think of the right way or better way to link the transaction to the select list.
renderCategory(category,transid){
console.log(category);
if (this.props.editMode === true){
return <div>
<select id="selCategory" onChange={this.props.onCategoryChange}>
const arrCategory = ["Category1","Category1","Category2","Category3","Category4","Category5","Category6","Category7"];
var i;
for (i = 1;arrCategory.length+1;i++){
<option value={transid+1}>{arrCategory[i-1]}</option>
if(arrCategory[i-1] === category) {
selectedIndex = i-1;
}
}
</select>
</div>
}
else {
return category
}
}
Here I have the code in the parent for handling the onChange event of the select list in my child.
handleCategoryChange(e) {
// this will have to be changed because I'm using the index value to store the transaction id
alert("The Child HTML is: " + e.target.innerHTML + "The selected value is: " + e.target.options[e.target.selectedIndex].value);
//// update the date which is in parent state to reflect the new value for category for the passed in transaction id (the problem is I don't know what the transaction id is...)
}
To achieve this, you need to do the following
In your parent component:
Maintain a state in your parent component which will store the data that has to be rendered in the child component.
Write a function in parent component which will update the state(i.e. the data to be rendered in the child component).
Then pass the data in your parent component's state and the state update function to child component via props.
In your child component:
Retrieve the data and the function passed by the parent component from the props of the child component.
Use the data to populate your table and to each editable box's input, pass an onChange and provide it the reference of the function passed from your parent component.
Here is a small snippet to take reference from:
class Parent extends Component {
constructor() {
super()
this.state = {
data: ''
}
}
//update your state here
stateUpdateHandler = (value) => {
this.setState({data: value})
}
render () {
return(
<Child data={this.state.data} stateUpdateHandler={stateUpdateHandler} />
)
}
}
class Child extends Component {
render() {
const {data, stateUpdateHandler}
return(
<div>
<input
type='text' value={d.value}
onChange={(e) => stateUpdateHandler(e.target.value)} />
</div>
)
}
}
EDIT: This is how you should handle onChange event
onCategoryChange: function(event) {
console.log(event.target.value) //This would have the value of transaction provided in the value attribute of option tag
}
If you want to get the transaction id and not the value in the value attribute of the option tag, you will have to change your select tag and write it like this:
<select id="" onChange={function() {this.props.onCategoryChange(transid)}}>

Resources