Skip to content

Commit

Permalink
Merge branch 'master' into releases/august
Browse files Browse the repository at this point in the history
  • Loading branch information
aliemir authored Aug 5, 2024
2 parents 9806a36 + bc03bff commit a237b51
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 9 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
paths:
- "packages/**"
- "examples/**"
- "cypress/e2e/**"
types:
- labeled
- synchronize
Expand Down
75 changes: 75 additions & 0 deletions cypress/e2e/i18n-nextjs/all.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/// <reference types="cypress" />
/// <reference types="../../cypress/support" />

Cypress.on("uncaught:exception", () => {
return false;
});

describe("i18n-nextjs", () => {
beforeEach(() => {
cy.clearAllCookies();
cy.clearAllLocalStorage();
cy.clearAllSessionStorage();

cy.interceptGETBlogPosts();
cy.visit("/");
});

it("should change", () => {
cy.wait("@getBlogPosts");

// check the elements are in English which is the default language
cy.get(".ant-menu > .ant-menu-item")
.contains("Blog Posts")
.get(".ant-page-header-heading-left")
.contains("Posts")
.get(".refine-create-button")
.contains("Create")
.get(".ant-table-thead > tr > :nth-child(2)")
.contains("Title")
.get(".ant-table-thead > tr > :nth-child(3)")
.contains("Content")
.get(".ant-table-thead > tr > :nth-child(4)")
.contains("Category")
.get(".ant-table-thead > tr > :nth-child(5)")
.contains("Status")
.get(".ant-table-thead > tr > :nth-child(6)")
.contains("Created At")
.get(".ant-table-thead > tr > :nth-child(7)")
.contains("Actions");

// find the language button
cy.get(".ant-layout-header > .ant-btn")
// should contain English which is the default language
.contains("English")
// hover over the button to show the dropdown
.trigger("mouseover")
// click on the German language
.get(".ant-dropdown-menu-title-content")
.contains("German")
.click()
// should contain German
.get(".ant-layout-header > .ant-btn")
.contains("German");

// check the elements are translated
cy.get(".ant-menu > .ant-menu-item")
.contains("Einträge")
.get(".ant-page-header-heading-left")
.contains("Einträge")
.get(".refine-create-button")
.contains("Erstellen")
.get(".ant-table-thead > tr > :nth-child(2)")
.contains("Titel")
.get(".ant-table-thead > tr > :nth-child(3)")
.contains("Inhalh")
.get(".ant-table-thead > tr > :nth-child(4)")
.contains("Kategorie")
.get(".ant-table-thead > tr > :nth-child(5)")
.contains("Status")
.get(".ant-table-thead > tr > :nth-child(6)")
.contains("Erstellt am")
.get(".ant-table-thead > tr > :nth-child(7)")
.contains("Aktionen");
});
});
63 changes: 63 additions & 0 deletions cypress/e2e/i18n-react/all.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/// <reference types="cypress" />
/// <reference types="../../cypress/support" />

Cypress.on("uncaught:exception", () => {
return false;
});

describe("i18n-react", () => {
beforeEach(() => {
cy.clearAllCookies();
cy.clearAllLocalStorage();
cy.clearAllSessionStorage();

cy.interceptGETPosts();
cy.visit("/");
});

it("should change", () => {
cy.wait("@getPosts");

// check the elements are in English which is the default language
cy.get(".ant-menu > .ant-menu-item")
.contains("Posts")
.get(".ant-page-header-heading-left")
.contains("Posts")
.get(".refine-create-button")
.contains("Create")
.get(".ant-table-thead > tr > :nth-child(2)")
.contains("Title")
.get(".ant-table-thead > tr > :nth-child(3)")
.contains("Category")
.get(".ant-table-thead > tr > :nth-child(4)")
.contains("Actions");

// find the language button
cy.get(".ant-layout-header > .ant-btn")
// should contain English which is the default language
.contains("English")
// hover over the button to show the dropdown
.trigger("mouseover")
// click on the German language
.get(".ant-dropdown-menu-title-content")
.contains("German")
.click()
// should contain German
.get(".ant-layout-header > .ant-btn")
.contains("German");

// check the elements are translated
cy.get(".ant-menu > .ant-menu-item")
.contains("Einträge")
.get(".ant-page-header-heading-left")
.contains("Einträge")
.get(".refine-create-button")
.contains("Erstellen")
.get(".ant-table-thead > tr > :nth-child(2)")
.contains("Titel")
.get(".ant-table-thead > tr > :nth-child(3)")
.contains("Kategorie")
.get(".ant-table-thead > tr > :nth-child(4)")
.contains("Aktionen");
});
});
4 changes: 2 additions & 2 deletions documentation/blog/2023-08-12-react-admin-vs-refine.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ The seed round, supported by investors such as Senovo, 500 Emerging Europe, Palm

In 2023, Refine also backed by YCombinator, solidifying its position as a promising venture.

With over 30K monthly active developers using it and an impressive 25K+ GitHub stars earned in just a year and a half, Refine has gained significant popularity within the developer community.
With over 32K+ monthly active developers using it and an impressive 27K+ GitHub stars earned in just a year and a half, Refine has gained significant popularity within the developer community.

According to [OSS Insight data](https://ossinsight.io/collections/react-framework/), since the beginning of 2023, it has consistently ranked in the top three of trending React frameworks and web frameworks.

Expand Down Expand Up @@ -406,7 +406,7 @@ When it comes to documentation, Refine has more useful resources.
## Community Engagement

<div className="centered-image">
<img style={{alignSelf:"center"}} src="https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-08-12-react-admin-vs-refine/star2.png" alt="react-admin-vs-refine" />
<img style={{alignSelf:"center"}} src="https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-08-12-react-admin-vs-refine/star-history-2.png" alt="react-admin-vs-refine" />
</div>

<br/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ description: We'll learn how to use Zustand in our React project.
slug: zustand-react-state
authors: chidume_nnamdi
tags: [dev-tools, react]
image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-05-18-zustand/social.png
image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-05-18-zustand/social-3.png
hide_table_of_contents: false
---

**This article was last updated on July 31, 2024, to add sections for Advanced State Management Techniques and Custom Hooks for Zustand.**

## Introduction

Redux changed the game in the global management system. It was so successful that it was widely adopted and used as the ideal state management system in any framework. Not only in the framework but its principle still serves greatly in software development. Almost all developers have used Redux to manage their global state, and we can all attest to how powerful, fast, and maintainable it is to use Redux as the global state management tool. It makes debugging very easy and our app is predictable.
Expand All @@ -22,6 +24,8 @@ Steps we'll cover:
- [What is Zustand?](#what-is-zustand)
- [Getting started with Zustand](#getting-started-with-zustand)
- [Build a To-do app using Zustand](#build-a-to-do-app-using-zustand)
- [Managing State Structures](#managing-state-structures)
- [Writing Custom Hooks](#writing-custom-hooks)

## What is Zustand?

Expand Down Expand Up @@ -425,12 +429,114 @@ const App = () => {
export default App;
```

<br/>
<div>
<a href="https://discord.gg/refine">
<img src="https://refine.ams3.cdn.digitaloceanspaces.com/website/static/img/discord_big_blue.png" alt="discord banner" />
</a>
</div>
## Managing State Structures

I was very eager to present advanced patterns of state management with Zustand, which I've come to investigate recently. These will help us deal with more complex state structures and improve application performance and maintainability.

Zustand helps us create nested state slices, so handling complex state structures is straightforward. Structuring the state in a more modular way makes it easier to update and manage some specific bits of state without impacting others.

Here is an example:

```javascript
const useStore = create((set) => ({
user: {
name: "",
age: 0,
address: {
street: "",
city: "",
},
},
updateUser: (newUser) =>
set((state) => ({ user: { ...state.user, ...newUser } })),
updateAddress: (newAddress) =>
set((state) => ({
user: {
...state.user,
address: { ...state.user.address, ...newAddress },
},
})),
}));
```

In this case, we had a nested state for the user and address, and therefore separate methods to update the fields of the user and address, respectively.

### Use Middleware with Zustand

Zustand can be enhanced with middleware to provide additional functionality, such as changes in state logging, the ability to save and load states in local storage, and handling asynchronous actions.

Below is how we may use middleware to log state changes:

```javascript
import { create } from "zustand";
import { devtools } from "zustand/middleware";

const useStore = create(
devtools((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
})),
);
```

Wrapping our store definition with the `devtools` configuration now allows state changes to be observable in the Redux DevTools extension, which is a great tool for debugging.

## Writing Custom Hooks

We'll see how to create custom hooks and utilities that would make state management easy with Zustand. They help in the reuse of logic for cleaner code in our React projects.

Custom hooks can encapsulate Zustand state logic, making it a lot easier to reuse and manage complex state interactions. Here's an example of how one can do that for managing user authentication state:

```javascript
import { create } from "zustand";

const useAuthStore = create((set) => ({
user: null,
login: (userData) => set({ user: userData }),
logout: () => set({ user: null }),
}));

const useAuth = () => {
const { user, login, logout } = useAuthStore();
return { user, login, logout };
};

export default useAuth;
```

With this custom hook, you may use it to manage the authentication state in any component by just calling `useAuth`.

### Constructing Utility Functions

Utility functions can also be used to simplify the state update and make code readability much easier. Below, take a look at an example with an updated nested state:

```javascript
const updateNestedState = (set, keyPath, value) => {
set((state) => {
const keys = keyPath.split(".");
let nestedState = state;
keys.slice(0, -1).forEach((key) => {
nestedState = nestedState[key];
});
nestedState[keys[keys.length - 1]] = value;
return { ...state };
});
};

const useNestedStateStore = create((set) => ({
data: {
user: {
profile: {
name: "",
},
},
},
updateProfileName: (name) =>
updateNestedState(set, "data.user.profile.name", name),
}));
```

That makes our task easier in updating state properties that are deeply nested and allows the use of the utility function in various parts of our application.

## Conclusion

Expand Down

0 comments on commit a237b51

Please sign in to comment.