I am creating a dashboard App with reactjs, ReactContextAPI ,apexChart where 1000 length json data will be visualized on a 7-8 different charts and it includes 6-7 variables filters.
Basic Idea about app structure: DefaultContext will be having 1000 arrays of object and the data will be synced to the whole component tree. two main sub component are FilterComponent (It Filters DefaultContext Data) and Chart Component (It consume DefaultContext Data).
Issue facing: suppose Filter Component add a filter and it remove some data from the defaultContext Data so that it can be synced to chartComponent.
Now I am confused of how am i going to get back Data(which was earlier removed from the DefaultContext) if we remove the filter.
How many approaches do i have to to it in a better way...
CodePudding user response:
Definitely a valid and common problem. The good news is that Context
is a good option.
Since you need to change the data, the Context has to have an interface that allow it to change the underneath data.
const DataContext = createContext({
data: [], // can have 1000 arrays later
changeData: () => {} // can change the array anytime
})
Now, we can wrap the provider with read/write functionalities.
const DataProvider = ({ children }) => {
const [data, changeData] = useState(initialData)
const value = { data, changeData }
return <DataContext.Provider value={value}>
{children}
</DataContext.Provider>
}
Now your app needs to be setup under this context provider:
const App = () => {
return <DataProvider>...</DataProvider>
}
To consume it,
const FilterOrChart = () => {
const { data, changeData } = useContext(DataContext)
...
}
In your Filter
, you want to invoke changeData
, and in your Chart
, you access data
.
To be honest, your case should be the one documented in the online documentation, unfortunately most of times, the tutorial only talks about the read operation, which is really not the point for the Context. A reading context is almost like chained prop, but writing context is a lot more powerful.
CodePudding user response:
The solution simply is to not filter your source of truth in the first place. This means not filtering the original state stored in the Context provider, but filtering it inline when rendering it downstream.
The state should be the minimal representation of your state, not including derived state. What is derived state? Think of the filtered result as being derived from the actual state and some filtering criteria. You are already storing the state in the Context, so you should now also need to store the filtering in state (somewhere, doesn't matter really). When you want to "restore" the original state you clear the filtering criteria so the data source is no longer filtered.
Example:
In this example the data
is external to the component filtering it. This is to represent the separation of the "state" from the filter states and the derived filtered result.
const data = [
{
id: "1",
color: "red",
shape: "round"
},
{
id: "2",
color: "green",
shape: "round"
},
{
id: "3",
color: "red",
shape: "square"
},
{
id: "4",
color: "green",
shape: "square"
},
{
id: "5",
color: "blue",
shape: "square"
}
];
function App() {
const [color, setColor] = React.useState("");
const [shape, setShape] = React.useState("");
return (
<div className="App">
<div className="container">
{data
.filter((el) => {
if (color && shape) {
return color === el.color && shape === el.shape;
}
if (color || shape) {
return color === el.color || shape === el.shape;
}
return true;
})
.map((el) => (
<div
key={el.id}
className={clsx("item", {
red: el.color === "red",
green: el.color === "green",
blue: el.color === "blue",
round: el.shape === "round"
})}
>
<p>{el.color}</p>
<p>{el.shape}</p>
</div>
))}
</div>
<div>
<label>
Shape
<select value={shape} onChange={(e) => setShape(e.target.value)}>
<option value="">All</option>
<option value="square">Square</option>
<option value="round">Round</option>
</select>
</label>
<label>
Color
<select value={color} onChange={(e) => setColor(e.target.value)}>
<option value="">All</option>
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
</label>
<button
type="button"
onClick={() => {
setColor("");
setShape("");
}}
>
Reset
</button>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
.App {
font-family: sans-serif;
text-align: center;
}
.container {
display: flex;
gap: 1rem;
padding: 1rem;
justify-content: center;
}
.item {
border: 1px solid;
height: 6rem;
width: 6rem;
}
.round {
border-radius: 50%;
}
.red {
background-color: red;
}
.green {
background-color: green;
}
.blue {
background-color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/clsx.min.js"></script>
<div id="root" />